Introduction
Dans le cadre de notre projet de statistiques, nous travaillerons sur
un jeu de données environnementales. Ce jeu de données est composé de
plusieurs fichiers qui contiennent des informations sur les oiseaux, les
stations de mesure, les caractéristiques des oiseaux, leurs régimes
alimentaires et la biodiversité.
Ce rapport a pour objectif de présenter les différentes analyses que
nous avons réalisées sur ces données. Il est composé de trois parties
principales dans lesquelles nous explorons les relations entre la
diversité des espèces d’oiseaux, l’artificialisation des sols et la
distance avec le centre-ville de Bordeaux. Nous avons également étudié
les régimes alimentaires des oiseaux et les modes de nidification en
fonction de certaines zones géographiques.
Vous trouverez des cartes, des graphiques, des tableaux, un sankie
plot ainsi qu’un sunburst plot qui nous permettront de découvrir la
mesure de la biodiversité.
L’ensemble de ces données sont tirées du travail des membres de
l’INRAE unité BioGeCo et du bureau de télédetection I-sea.
Vous trouverez de nombreux résultats comme plusieurs courbes de
diversité révélant la baisse significative de diversité dans les lieux
les plus artificialisés. Vous trouverez également l’entièreté de notre
cheminement pour arriver à nos résultats statistiques, notamment
concernant la distribution des espèces d’oiseaux observées en Gironde,
mais aussi des statistiques multivariées et descriptives.
Nous vous invitons à découvrir l’ensemble de nos analyses et à
explorer les différentes visualisations que nous avons réalisées pour
mieux comprendre les relations entre les différentes variables de notre
jeu de données. Toutes nos visualisations sont interactives et vous
permettent de zoomer, de déplacer et de cliquer sur les différents
éléments pour obtenir plus d’informations. Cela vous permettra d’avoir
une expérience plus immersive et vous permettra de créer vos propres
analyses, en parallèle à celles réalisées par nos soins.
Exploration des données
Pour commencer, nous chargeons les données et nous les explorons pour
mieux comprendre leur structure. Dans un premier temps, nous allons
ajouter une colonne à notre DataFrame qui contient les noms latins des
oiseaux afin de facilliter les analyses par la suite.
oiseaux <- read.csv("data/birds/Oiseaux_up_to_2023.csv", header = TRUE, sep = "\t")
# CRÉER UNE NOUVELLE COLONNE DANS LE DATAFRAME OISEAUX QUI CONTIENT LE NOM LATIN DE L'OISEAU
my_split <- function(array, str = " \\| ") {
out <- rep(NA, length(array))
for (i in 1:length(array)) {
out[i] <- unlist(strsplit(array[i], str))[1]
}
return(out)
}
only_latin <- my_split(as.vector(oiseaux$Nom_Taxon_Cite))
oiseaux$latin <- only_latin
oiseaux$annee <- as.numeric(substr(oiseaux$Date, 1, 4))
Par la suite, nous allons explorer les données pour mieux comprendre
la distribution des espèces d’oiseaux observées. Voici un tableau qui
montre les 10 espèces d’oiseaux les plus fréquemment observées dans
l’ensemble des données.
# LES ESPÈCES D'OISEAUX LES PLUS FRÉQUENTES OBSERVÉES DANS L'ENSEMBLE DES DONNÉES
as.data.frame(sort(table(oiseaux$latin), decreasing = TRUE)[1:10])
Nous allons maintenant explorer la fréquence des espèces d’oiseaux
observées dans l’ensemble des données par année. Voici, par ordre
alphabétique, le tableau mettant en évidence cette fréquence.
# FRÉQUENCE DES ESPÈCES D'OISEAUX OBSERVÉES DANS L'ENSEMBLE DES DONNÉES PAR ANNÉE
Annee <- my_split(as.vector(oiseaux$Date), str = "-")
oiseaux$Annee <- as.factor(Annee)
as.data.frame.matrix(table(oiseaux$latin, oiseaux$Annee))
Mesure de la diversité
Dans cette partie, nous allons explorer la mesure de la diversité à à
l’aide de plusieurs entropies. Nous nous intéresserons uniquement au
MOS11, c’est à dire les surfaces artificialisées. On prend comme buffer
size 500m.
Dans un premier temps, nous allons utiliser la proportion d’espèces
différentes observées dans une station pour mesurer la diversité. Dans
un deuxième temps, nous allons utiliser l’entropie de Shannon et enfin
l’indice de Simpson.
## Analyse de diversité par rapport à MOS11 et par année
denombrement <- oiseaux %>%
group_by(Code_Maille, annee, latin) %>%
summarise(sum = sum(Denombrement_min, na.rm = TRUE), .groups = "drop") %>%
arrange(desc(Code_Maille))
denombrement$p <- rep(NA, nrow(denombrement))
for (i in 1:nrow(denombrement)) {
numerator <- denombrement$sum[i]
denominator <-
sum(denombrement$sum[which(denombrement$Code_Maille == denombrement$Code_Maille[i]
& denombrement$annee == denombrement$annee[i])])
denombrement$p[i] <- numerator / denominator
}
index <- denombrement %>%
group_by(Code_Maille, annee = factor(annee)) %>%
summarise(D1 = sum(p > 0, na.rm = TRUE),
D2 = exp(-sum(p * log(p))),
D3 = 1 / sum(p^2), .groups = "drop") %>%
arrange(desc(Code_Maille))
LUP <- read.csv("data/birds/LandUsePer_BM_2023_cartoISea.csv", header = TRUE)
index$MOS11 <- rep(NA, nrow(index))
for (i in 1:nrow(index)) {
index$MOS11[i] <- LUP$MOS11[which(index$Code_Maille[i] == LUP$ID & LUP$BufferSize == 500)]
}
Proportion d’espèces
Voici le graphique qui montre la relation entre la diversité et MOS11
pour chaque année expliquée par la proportion d’espèces.
ggplot(index, aes(x = MOS11, y = D1, color = as.factor(annee))) +
geom_point(size = 2) +
geom_smooth(method = "auto", se = TRUE, color = "black", alpha = 0.2) +
labs(title = "Proportion d’espèces en fonction de MOS11",
x = "MOS11",
y = "Proportion d'espèces") +
theme_minimal() +
labs(colour = "Année") +
theme(legend.position = "bottom")

Indice de Shanon
Ceci est le graphique qui montre la relation entre la diversité et
MOS11 pour chaque année expliquée par l’entropie de Shannon.
ggplot(index, aes(x = MOS11, y = D2, color = as.factor(annee))) +
geom_point(size = 2) +
geom_smooth(method = "auto", se = TRUE, color = "black", alpha = 0.2) +
labs(title = "Indice de Shanon en fonction de MOS11",
x = "MOS11",
y = "Indice de Shanon") +
theme_minimal() +
labs(colour = "Année") +
theme(legend.position = "bottom")

Indice de Simpson
Ceci est le graphique qui montre la relation entre la diversité et
MOS11 pour chaque année expliquée par l’indice de Simpson.
ggplot(index, aes(x = MOS11, y = D3, color = as.factor(annee))) +
geom_point(size = 2) +
geom_smooth(method = "auto", se = TRUE, color = "black", alpha = 0.2) +
labs(title = "Indice de Simpson en fonction de MOS11",
x = "MOS11",
y = "Indice de Simpson") +
theme_minimal() +
labs(colour = "Année") +
theme(legend.position = "bottom")

La tendance globale est l’augmentation de la diversité au début de la
courbe jusqu’à atteindre un maximum puis une baisse quand le MOS11
augmente davantage. C’est-à-dire que, moins les sols sont
artificialisés, plus la diversité est grande avec une diversité maximale
atteinte quand le milieu est à la fois artificialisé mais présente
également des surfaces non artificialisées.
Notre deuxième partie se portera sur l’analyse de la diversité des
espèces d’oiseaux en fonction de la distance avec le centre-ville de
Bordeaux. On observera une tendance de diversité par rapport à la
distance de la maille avec le centre ville qui diffère légèrement par
rapport aux résultats ci-dessus. Pour ce faire, nous allons calculer
toutes les distances de chaque maille à PeyBerland, qui sera notre point
référent pour le centre.
Analyses intéractives et géographiques
oiseaux <- read.csv("data/birds/Oiseaux_up_to_2023.csv", header = TRUE, sep = "\t")
# CREATE A NEW COLUMN IN THE OISEAUX DATAFRAME THAT CONTAINS THE LATIN NAME OF THE BIRD
my_split <- function(array, str = " \\| ") {
out <- rep(NA, length(array))
for (i in 1:length(array)) {
out[i] <- unlist(strsplit(array[i], str))[1]
}
return (out)
}
only_latin <- my_split(as.vector(oiseaux$Nom_Taxon_Cite))
# length(unique(only_latin)) == length(unique(oiseaux$Code_Ref))
oiseaux$latin <- only_latin
oiseaux$annee <- as.numeric(substr(oiseaux$Date, 1, 4))
denombrement <- oiseaux %>%
group_by(Code_Maille, annee, latin) %>%
summarise(sum = sum(Denombrement_min, na.rm = TRUE), .groups = 'drop') %>%
arrange(desc(Code_Maille))
denombrement$p <- rep(NA, nrow(denombrement))
for (i in 1:nrow(denombrement)) {
numerator <- denombrement$sum[i]
denominator <-
sum(denombrement$sum[which(denombrement$Code_Maille == denombrement$Code_Maille[i]
& denombrement$annee == denombrement$annee[i])])
denombrement$p[i] <- numerator / denominator
}
index <- denombrement %>%
group_by(Code_Maille, annee = factor(annee)) %>%
summarise(D1 = sum(p > 0, na.rm = TRUE),
D2 = exp(-sum(p*log(p))),
D3 = 1 / sum(p^2), .groups = 'drop') %>%
arrange(desc(Code_Maille))
LUP <- read.csv("data/birds/LandUsePer_BM_2023_cartoISea.csv", header = TRUE)
index$MOS11 <- rep(NA, nrow(index))
for (i in 1:nrow(index)) {
index$MOS11[i] <- LUP$MOS11[which(index$Code_Maille[i] == LUP$ID & LUP$BufferSize == 1000)]
}
traits <- read.csv("data/birds/traits-statut-IUCN-biodivercite.csv", header = TRUE)
cite <- read.csv("data/birds/BiodiverCite_sites.csv", header = TRUE, sep=";")
#on veut traiter la table dénombrement qu'on appelera denombrementCarte où il restera pour chaque maille et pour chaque année le top 3 des oiseaux les plus observés sans la variable p
denombrementCarte <- denombrement %>%
group_by(Code_Maille, annee) %>%
top_n(3, sum) %>%
arrange(Code_Maille, annee, desc(sum)) %>%
ungroup() %>%
select(-p)
#on veut changer cette table pour faire apparaitre de nouvelles colonnes pour qu'il y ait : une ligne par maille et par année, et pour chaque oiseau, le nombre d'individus observés
denombrementCarte <- denombrementCarte %>%
pivot_wider(names_from = latin, values_from = sum, values_fill = 0)
#on veut ajouter les colonnes geometry, buffer size et MOS11 à la table denombrementCarte et pas les autres
denombrementCarte <- left_join(denombrementCarte, LUP, by = c("Code_Maille" = "ID"))
#on enlève les colonnes : MOS1, MOS2, MOS3, MOS4, MOS5, MOS6, MOS7, MOS8, MOS9, MOS10, MOS12, MOS13, MOS14
denombrementCarte <- denombrementCarte %>%
select(-c(MOS1, MOS2, MOS3, MOS4, MOS5, MOS6, MOS7, MOS8, MOS9, MOS10, MOS12, MOS13, MOS14))
# On garde que les lignes ou BufferSize == 500
denombrementCarte <- denombrementCarte %>%
filter(BufferSize == 500)
denombrementCarte <- st_as_sf(denombrementCarte, coords = c("X", "Y"), crs = 2154)
denombrementCarte <- st_transform(denombrementCarte, crs = 4326)
# On garde seuelement les valeurs ou annee = 2018
denombrementCarte <- denombrementCarte %>%
filter(annee == 2023)
denombrementCarte
Grâce à ce tableau, nous pouvons voir comment les données sont
structurées et remarquer notamment qu’il classe le nombre d’oiseaux par
maille et par année.
Dans la suite de ce rapport vous trouverez des analyses géographiques
et interactives qui vous permettront de visualiser comment les oiseaux
de la Gironde évoluent en fonction de l’artificialisation des sols. Vous
y trouverez des analyses sur les régimes alimentaires des oiseaux, les
niveaux de spécialisation des oiseaux en fonction de leur habitat
(Maille).
Voici la carte interactive qui montre les mailles de mesure et les
oiseaux les plus observés dans chaque maille. Nous vous invitons à
cliquer sur les cercles pour obtenir toutes les informations sur les
oiseaux.
# On garder que les colonnes qui nous intéressent, cad code_site et Nom_lieu
cite <- read.csv("data/birds/BiodiverCite_sites.csv", header = TRUE, sep=";")
cite <- cite %>%
select(code_site, Nom_lieu)
#On join les deux tables cite et denombrementCarte
denombrementCarte <- left_join(denombrementCarte, cite, by = c("Code_Maille" = "code_site"))
# Créer une chaîne de caractères pour les popups avec le top trois des oiseaux ayant la plus grande valeur
denombrementCarte$popup_text <- paste0("<strong>Maille:</strong> ", denombrementCarte$Nom_lieu, "<br>",
"<strong>Année:</strong> ", denombrementCarte$annee, "<br>",
"<strong>Top 3 des oiseaux:</strong>", "<br>")
dataframe <- as.data.frame(denombrementCarte)
# On enleve les colonnes Code_Maille, annee, BufferSize, MOS11, Geometry, geometry
dataframe <- dataframe %>%
select(-c(Code_Maille, annee, BufferSize, MOS11, Geometry, geometry, Nom_lieu))
# Ajouter les noms des oiseaux et leurs valeurs au popup_text pour chaque ligne
for (i in 1:nrow(dataframe)) {
top_birds <- sort(unlist(dataframe[i, -c(ncol(dataframe))]), decreasing = TRUE)[1:3]
top_bird_names <- names(top_birds)
denombrementCarte$popup_text[i] <- paste0(denombrementCarte$popup_text[i],
top_bird_names[1], ": ", top_birds[1], " - ", traits[which(traits$Nom.latin == top_bird_names[1]), "Niveau.de.spécialisation"], "<br>",
top_bird_names[2], ": ", top_birds[2], " - ", traits[which(traits$Nom.latin == top_bird_names[2]), "Niveau.de.spécialisation"], "<br>",
top_bird_names[3], ": ", top_birds[3], " - ", traits[which(traits$Nom.latin == top_bird_names[3]), "Niveau.de.spécialisation"])
}
pal <- colorNumeric("viridis", domain = denombrementCarte$MOS11)
leaflet(data = denombrementCarte) %>%
addProviderTiles("CartoDB.Positron") %>%
addCircles(radius = 300, color = ~pal(MOS11), fillOpacity = 0.2, popup = ~popup_text) %>%
addLegend("bottomright", pal = pal, values = ~MOS11, title = "MOS11", position = "bottomright") %>%
addScaleBar(position = "bottomleft")
Grâce à cette carte, nous obtenons le classement (Top 3) des oiseaux
les plus observés dans chaque maille de mesure pour l’année 2023. Nous
avons également ajouté une information supplémentaire sur chaque oiseau
pour mettre en évidence son niveau de spécialisation afin de mieux
comprendre de quel type de milieu il a besoin pour vivre et la
géolocalisation sur la Gironde.
Nous pouvons voir que le niveau de spécialisation le plus répendu est
généraliste car on en retrouve au centre-ville et en périphérie.
Egalement, au centre ville, on retrouve des oiseaux de type Bâti et en
périphérie loins du centre-ville, on retrouve des oiseaux de type Fôret
et Agricole. Proche des points d’eau, on trouve des oiseaux de type Zone
humide.
Ainsi, les zones qui combinent à la fois végétation et bâtiments
seraient plus susceptibles d’accueillir des oiseaux avec des niveaux de
spécialisation variés, ce qui expliquerait l’augmentation du taux de
diversité observée dans les zones où se mélangent espaces verts et zones
urbanisées.
Il nous semblait intéressant de savoir quels étaient les régimes
alimentaires majoritaires principaux en fonction de chaque maille. Nous
avons donc créé un graphique qui montre la répartition des régimes
alimentaires pour chaque maille.
# changed the unmatchinng latin names to the correct ones to match alimentation
denombrement$latin[denombrement$latin == "Carduelis chloris"] <- "Chloris chloris"
denombrement$latin[denombrement$latin == "Carduelis spinus"] <- "Spinus spinus"
denombrement$latin[denombrement$latin == "Casmerodius albus"] <- "Ardea alba"
denombrement$latin[denombrement$latin == "Carduelis cannabina"] <- "Linaria cannabina"
no_info <- c("Himantopus himantopus", "Tringa ochropus",
"Caprimulgus europaeus", "Lanius senator",
"Dryocopus martius", "Emberiza calandra")
alimentation <- read.csv("data/birds/traits-statut-IUCN-biodivercite.csv", header = TRUE)
denombrement$regime_alimentaire <- rep(NA, nrow(denombrement))
for (i in 1:nrow(denombrement)) {
if (denombrement$latin[i] %in% no_info){
denombrement$regime_alimentaire[i] <- NA
}
else {
denombrement$regime_alimentaire[i] <-
alimentation$Régime.alimentaire[which(alimentation$Nom.latin == denombrement$latin[i])]
}
}
denombrement$regime_alimentaire <- as.factor(denombrement$regime_alimentaire)
plot_data = function (data, title) {
ggplot(data, aes(x="", y=sum, fill=regime_alimentaire)) +
geom_bar(stat="identity", width=0.1) +
coord_polar("y", start=0) +
ggtitle(paste("Station: ", as.character(title))) +
theme_void()
}
cite <- cite %>%
select(code_site, Nom_lieu)
#On join les deux tables cite et denombrementCarte
denombrement <- left_join(denombrement, cite, by = c("Code_Maille" = "code_site"))
N <- length(unique(denombrement$Code_Maille))
p <- vector("list", length = N)
for (i in 1:N) {
data <- denombrement[which(denombrement$Code_Maille == unique(denombrement$Code_Maille)[i]),]
data <- data %>% group_by(regime_alimentaire) %>% summarise(sum = sum(p, na.rm = TRUE))
p[[i]] <- plot_data(data, unique(denombrement$Nom_lieu)[i])
}
coordinates <- st_as_sf(LUP, coords = c("X", "Y"), crs = 2154)
coordinates <- st_transform(coordinates, crs = 4326)
coordinates <- coordinates[coordinates$BufferSize == 500,]
# Remove the coordinates that are not in denombrement
for (i in seq_along(coordinates$ID)){
if (!(coordinates$ID[i] %in% unique(denombrement$Code_Maille))){
coordinates <- coordinates[-i,]
}
}
coordinates <- coordinates[order(coordinates$ID, decreasing = TRUE),]
pal <- colorNumeric("viridis", domain = coordinates$MOS11)
leaflet(data = coordinates) %>%
addProviderTiles("CartoDB.Positron") %>%
addCircles(radius = 300, color = ~pal(MOS11),
fillOpacity = 0.5, group = "pnt") %>%
addLegend("bottomright", pal = pal, values = coordinates$MOS11, title = "MOS11") %>%
addScaleBar(position = "bottomleft") %>%
addPopupGraphs(p, width = 200, height = 200, group = "pnt")
Voici à quoi ressemble la carte interactive qui montre les régimes
alimentaires majoritaires principaux en fonction de chaque maille. Nous
pouvons voir qu’en général, le régime alimentaire dominant est le régime
alimentaire mixte, que cela soit au centre de Bordeaux ou en périphérie.
Il semble donc que le régime alimentaire de l’oiseau ne soit pas
véritablement impacté par l’artificialisation des sols. Essayons
maintenant d’étudier d’autre caratéristiques afin d’essayer de trouver
des raisons à ce pic de diversité.
denombrement$Nidification <- rep(NA, nrow(denombrement))
for (i in 1:nrow(denombrement)) {
if (denombrement$latin[i] %in% no_info){
denombrement$Nidification[i] <- NA
}
else {
denombrement$Nidification[i] <-
alimentation$Nidification[which(alimentation$Nom.latin == denombrement$latin[i])]
}
}
denombrement$Nidification <- as.factor(denombrement$Nidification)
plot_data = function (data, title) {
ggplot(data, aes(x="", y=sum, fill=Nidification)) +
geom_bar(stat="identity", width=0.1) +
coord_polar("y", start=0) +
ggtitle(paste("Station: ", as.character(title))) +
theme_void()
}
#On join les deux tables cite et denombrementCarte
N <- length(unique(denombrement$Code_Maille))
p <- vector("list", length = N)
for (i in 1:N) {
data <- denombrement[which(denombrement$Code_Maille == unique(denombrement$Code_Maille)[i]),]
data <- data %>% group_by(Nidification) %>% summarise(sum = sum(p, na.rm = TRUE))
p[[i]] <- plot_data(data, unique(denombrement$Nom_lieu)[i])
}
coordinates <- st_as_sf(LUP, coords = c("X", "Y"), crs = 2154)
coordinates <- st_transform(coordinates, crs = 4326)
coordinates <- coordinates[coordinates$BufferSize == 500,]
# Remove the coordinates that are not in denombrement
for (i in seq_along(coordinates$ID)){
if (!(coordinates$ID[i] %in% unique(denombrement$Code_Maille))){
coordinates <- coordinates[-i,]
}
}
coordinates <- coordinates[order(coordinates$ID, decreasing = TRUE),]
pal <- colorNumeric("viridis", domain = coordinates$MOS11)
leaflet(data = coordinates) %>%
addProviderTiles("CartoDB.Positron") %>%
addCircles(radius = 300, color = ~pal(MOS11),
fillOpacity = 0.5, group = "pnt2") %>%
addLegend("bottomright", pal = pal, values = coordinates$MOS11, title = "MOS11") %>%
addScaleBar(position = "bottomleft") %>%
addPopupGraphs(p, width = 200, height = 200, group = "pnt2")
Cette carte illustre la répartition des types de nidification en
fonction des zones géographiques. Il est observé que dans les zones
centrales, la majorité des nids sont situés dans des cavités, ce qui
peut être attribué à la densité élevée de bâtiments et à la rareté de la
végétation en ville. Le mode de nidification au sol est le moins courant
dans ces zones, probablement en raison de la faible probabilité de
survie des oiseaux dans un environnement urbain avec un nid au sol.
En périphérie, les modes de nidification semblent plus diversifiés,
avec une prédominance de la nidification en buisson. La nidification
dans les arbres est également très répandue, et on retrouve aussi la
nidification en cavité. Le fait de ne pas être situé directement au
centre-ville semble offrir plus de possibilités de nidification, ce qui
pourrait expliquer une plus grande diversité d’espèces si on s’éloigne
légèrement du centre ville, grâce à une combinaison de bâtiments et de
végétation offrant plus de choix pour la nidification.
Pour aller plus loin
Cette section est dédiée à des analyses plus poussées qui pourraient
être réalisées pour mieux comprendre les données et les relations entre
les différentes variables. Vous trouverez un Parallel Coordinates Plot
ainsi qu’un sun burst plot qui illustrent les caractéristiques des
différentes espèces d’oiseaux.
Voici, ci-dessous le Parallel Coordinates Plot intéractif. Vous
pouvez cliquer sur les différentes espèces pour obtenir plus
d’informations sur chacune d’elles.
# --- Load data ---
birds_info <- read.csv("data/birds/traits-statut-IUCN-biodivercite.csv", header = TRUE)
birds_info <- birds_info[, c(
"Nom.latin",
"Niveau.de.spécialisation",
"Régime.alimentaire",
"Technique.d.alimentation",
"Nidification",
"Période.de.migration"
)]
# --- Parcoords data preparation ---
unique_level <- unique(birds_info[, "Niveau.de.spécialisation"])
unique_regime <- unique(birds_info[, "Régime.alimentaire"])
unique_technique <- unique(birds_info[, "Technique.d.alimentation"])
unique_nidi <- unique(birds_info[, "Nidification"])
unique_migration <- unique(birds_info[, "Période.de.migration"])
plot_ly(data.frame(), type = "parcoords", line = list(color = "blue"), height = 950,
dimensions = list(
list(
label = "Espèces",
range = c(0, length(birds_info[, "Nom.latin"]) + 1),
tickvals = 1:length(birds_info[, "Nom.latin"]),
ticktext = birds_info[, "Nom.latin"],
values = 1:length(birds_info[, "Nom.latin"])
),
list(
label = "Technique d'alimentation",
range = c(0, length(unique_technique) + 1),
tickvals = as.numeric(factor(unique_technique)),
ticktext = unique_technique,
values = as.numeric(factor(birds_info[, "Technique.d.alimentation"]))
),
list(
label = "Nidification",
range = c(0, length(unique_nidi) + 1),
tickvals = as.numeric(factor(unique_nidi)),
ticktext = unique_nidi,
values = as.numeric(factor(birds_info[, "Nidification"]))
),
list(
label = "Niveau de spécialisation",
range = c(0, length(unique_level) + 1),
tickvals = as.numeric(factor(unique_level)),
ticktext = unique_level,
values = as.numeric(factor(birds_info[, "Niveau.de.spécialisation"]))
),
list(
label = "Période de migration",
range = c(0, length(unique_migration) + 1),
tickvals = as.numeric(factor(unique_migration)),
ticktext = unique_migration,
values = as.numeric(factor(birds_info[, "Période.de.migration"]))
),
list(
label = "Régime alimentaire",
range = c(0, length(unique_regime) + 1),
tickvals = as.numeric(factor(unique_regime)),
ticktext = unique_regime,
values = as.numeric(factor(birds_info[, "Régime.alimentaire"]))
)
)
) %>% layout(
title = "Caractéristiques des différentes espèces d'oiseaux",
margin = list(l = 140, r = 55, b = 0)
)
Exemple d’utilisation :
- Nous voulons savoir le régime alimentaire des oiseaux qui migrent.
Pour cela, il suffit de cliquer au niveau de “migrateur tardif” et de
cliquer sur “Végétarien”, “Mixte” ou “carnivore” pour obtenir les
espèces d’oiseaux qui correspondent à ces critères. Ceci est très utile
lorsqu’on veut comparer le nombre d’oiseau selon les caractéristiques
choisies ou même simplement rechercher l’espèce qui correspond à
certains critères que l’on veut étudier. Pour finir l’exemple, si on
clique au niveau de “Migrateur tardif”, on remarque qu’aucun oiseau
n’est végétarien. De plus, on peut affiner notre sélection pour voir
quelles espèces d’oiseaux sont des migrateurs tardifs et font leur nid
dans les buissons. On clique ainsi sur buisson et on voit que l’espèce
“Lanius collurio” correspond à ces critères. C’est une espèce carnivore
qui migre tardivement et fait son nid dans les buissons qui a un niveau
de spécialisation agricole et qui s’alimente en vol.
Voici une image de la pie-grièche écorcheur
(Lanius collurio), wikipedia
# --- Load data ---
LUP <- read.csv("data/birds/LandUsePer_BM_2023_cartoISea.csv", header = TRUE)
LUP <- LUP[, c(c("ID", "BufferSize"), paste0("MOS", 1:14))]
birds_obs <- read.csv("data/birds/Oiseaux_up_to_2023.csv", header = TRUE, sep = "\t")
birds_obs <- birds_obs[, c(c("Code_Maille", "Date", "Nom_Taxon_Cite", "Denombrement_min"))]
birds_obs <- birds_obs %>%
rename(Year = Date, ID = Code_Maille, Latin = Nom_Taxon_Cite)
birds_obs[, "Year"] <- substr(birds_obs[, "Year"], start = 1, stop = 4)
birds_obs[, "Latin"] <- sapply(strsplit(birds_obs[, "Latin"], split = " | ", fixed = TRUE), function(x) x[1])
birds_obs <- birds_obs %>%
group_by(ID, Year, Latin) %>%
summarise(Sum = sum(Denombrement_min), .groups = "drop")
LUP_birds_obs <- merge(LUP, birds_obs, by = "ID")
# --- Sunburst data preparation ---
unique_year <- unique(LUP_birds_obs[, "Year"])
unique_year <- unique_year[order(unique_year)]
ids <- c("Année", unique_year)
for (year in unique_year) {
ids <- c(ids, paste0(year, paste0(" - MOS", 1:14)))
}
for (year in unique_year) {
for (mos in paste0("MOS", 1:14)) {
for (specie in birds_info[, "Nom.latin"]) {
ids <- c(ids, paste0(year, paste0(paste0(" - ", mos), paste0(" - ", specie))))
}
}
}
labels <- c("Année", unique_year, rep(paste0("MOS", 1:14), times = length(unique_year)))
labels <- c(labels, rep(birds_info[, "Nom.latin"], times = 14 * length(unique_year)))
parents <- c("", rep("Année", times = length(unique_year)), rep(unique_year, each = 14))
for (year in unique_year) {
parents <- c(parents, paste0(year, rep(paste0(" - MOS", 1:14), each = length(birds_info[, "Nom.latin"]))))
}
values <- c(c(length(unique_year) * 14000, rep(14000, times = length(unique_year))), rep(1000, times = length(unique_year) * 14))
for (year in unique_year) {
for (mos in paste0("MOS", 1:14)) {
for (specie in birds_info[, "Nom.latin"]) {
tmp <- LUP_birds_obs[LUP_birds_obs["Year"] == year & LUP_birds_obs["Latin"] == specie & LUP_birds_obs["BufferSize"] == 500, ]
values <- c(values, sum(tmp[mos] * tmp["Sum"]))
}
tail_values <- values[(1 + length(values) - length(birds_info[, "Nom.latin"])):length(values)]
values[(1 + length(values) - length(birds_info[, "Nom.latin"])):length(values)] <- tail_values / sum(tail_values) * 1000
}
}
is_nan_values <- !is.na(values) & values != 0
ids <- ids[is_nan_values]
labels <- labels[is_nan_values]
parents <- parents[is_nan_values]
values <- values[is_nan_values]
# --- Sunburst display ---
plot_ly(
ids = ids,
labels = labels,
parents = parents,
values = values,
type = "sunburst",
branchvalues = "total",
maxdepth = 2,
insidetextorientation = "radial",
hoverinfo = "label+percent entry",
height = 800
) %>% layout(
title = list(
text = "Proportion des espèces d'oiseaux les plus couramment observées<br>en fonction de l'année et du MOS",
y = 1.1
),
margin = list(t = 100)
)
Conclusion
Après avoir mené une étude approfondie sur la diversité des oiseaux à
Bordeaux et sa périphérie, nous avons pu faire plusieurs découvertes
intéressantes en utilisant différentes mesures de diversité telles que
la richesse spécifique, l’indice de Shannon et l’indice de Simpson.
Tout d’abord, nous avons découvert que la diversité des espèces
d’oiseaux est étroitement liée au niveau d’artificialisation des sols,
mesuré par la variable MOS11. En effet, nous avons constaté que la
diversité est plus élevée dans les zones où se mélangent espaces verts
et zones urbanisées. Cette observation suggère que les zones urbaines
qui préservent des espaces verts et des habitats naturels sont plus
susceptibles d’accueillir une grande diversité d’espèces d’oiseaux.
Ensuite, nous avons étudié la relation entre la diversité des espèces
d’oiseaux et la distance au centre-ville. Nous avons constaté que la
diversité est plus grande lorsque la maille se trouve à environ 9 km du
centre-ville. Cette tendance s’explique en partie par le fait que les
zones situées à cette distance du centre-ville sont souvent des zones de
transition entre les espaces urbains et les espaces ruraux, offrant
ainsi une variété d’habitats pour les oiseaux.
Par la suite, nous avons essayé de trouver des liens entre la
diversité des espèces d’oiseaux et d’autres caractéristiques telles que
les régimes alimentaires, les niveaux de spécialisation et les modes de
nidification.
Pour les niveaux de spécialisation, nous avons observé que les
oiseaux de type Bâti sont plus répandus dans les zones urbaines, tandis
que les oiseaux de type Forêt et Agricole sont plus fréquents en
périphérie. Il y aussi de nombreuses espèces de type Zone humide près
des points d’eau. Il y a de nombreuses espèces généralistes au
centre-ville et en périphérie. Ces observations suggèrent que la
diversité des espèces est influencée par les caractéristiques des
habitats.
Nous avons également étudié les régimes alimentaires des oiseaux et
avons constaté que le régime alimentaire de l’oiseau ne semble pas être
impacté par l’artificialisation des sols. Cette observation suggère que
les oiseaux sont capables de s’adapter à leur environnement et de
trouver de la nourriture même dans les zones urbaines.
Enfin, nous avons étudié les modes de nidification des oiseaux et
avons constaté que les zones qui combinent à la fois végétation et
bâtiments sont plus susceptibles d’accueillir des oiseaux avec des modes
de nidification variés. Par exemple, les zones centrales sont propices à
la nidification en cavité en raison de la densité élevée de bâtiments,
tandis que les zones périphériques offrent plus de possibilités de
nidification en buisson et dans les arbres.
Cette observation souligne l’importance de préserver et de restaurer les
habitats naturels dans les zones urbaines pour maintenir un niveau
maximal de diversité des espèces d’oiseaux.
En conclusion, notre étude a permis de mettre en évidence
l’importance de préserver et de restaurer les habitats naturels dans les
zones urbaines pour maintenir la biodiversité des oiseaux. Nous avons
constaté que les zones de transition entre les espaces urbains et ruraux
sont des zones clés pour la diversité des espèces d’oiseaux. Nous avons
également observé que les oiseaux sont capables de s’adapter à leur
environnement et de trouver de la nourriture même dans les zones
urbaines. Ces résultats soulignent l’importance de prendre en compte la
biodiversité dans les politiques d’aménagement urbain et de préserver
les habitats naturels pour maintenir la diversité des espèces d’oiseaux.
En utilisant différentes mesures de diversité, nous avons pu confirmer
ces résultats et renforcer la validité de notre étude.
mos_table <- read.csv("data/birds/themes_mos_gir.txt", header = TRUE, sep = "\t")
mos_table <- mos_table[, c("ID", "MOS")]
mos_table["ID"] <- paste0("MOS", mos_table[, "ID"])
mos_table
LS0tDQp0aXRsZTogIlByb2pldCBzdGF0IHBvdXIgZG9ubsOpZXMgZW52aXJvbm5lbWVudGFsZXMiDQphdXRob3I6ICJBbGV4YW5kcmUgTGV5cywgQmFwdGlzdGUgR2VyYm91aW4sIEhhbWFkIFRyaWEsIExvdWlzIERlbGlnbmFjLCBUaMOpbyBMYXZhbmRpZXIiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiwgJVknKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB1bml0ZWQNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICMgcGRmX2RvY3VtZW50Og0KICAjICAgdG9jOiB0cnVlDQogICMgICB0b2NfZGVwdGg6IDINCiAgIyAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAjICAgZGZfcHJpbnQ6IGthYmxlDQogICMgICBoaWdobGlnaHQ6IHRhbmdvDQotLS0NCg0KPHN0eWxlPg0KICAudG9jaWZ5LWV4dGVuZC1wYWdlIHsNCiAgICBkaXNwbGF5OiBub25lOw0KICB9DQo8L3N0eWxlPg0KDQojIEludHJvZHVjdGlvbg0KDQoNCkRhbnMgbGUgY2FkcmUgZGUgbm90cmUgcHJvamV0IGRlIHN0YXRpc3RpcXVlcywgbm91cyB0cmF2YWlsbGVyb25zIHN1ciB1biBqZXUgZGUgZG9ubsOpZXMgZW52aXJvbm5lbWVudGFsZXMuDQpDZSBqZXUgZGUgZG9ubsOpZXMgZXN0IGNvbXBvc8OpIGRlIHBsdXNpZXVycyBmaWNoaWVycyBxdWkgY29udGllbm5lbnQgZGVzIGluZm9ybWF0aW9ucyBzdXIgbGVzIG9pc2VhdXgsIGxlcyBzdGF0aW9ucyBkZSBtZXN1cmUsIGxlcyBjYXJhY3TDqXJpc3RpcXVlcyBkZXMgb2lzZWF1eCwgbGV1cnMgcsOpZ2ltZXMgYWxpbWVudGFpcmVzIGV0IGxhIGJpb2RpdmVyc2l0w6kuDQoNCkNlIHJhcHBvcnQgYSBwb3VyIG9iamVjdGlmIGRlIHByw6lzZW50ZXIgbGVzIGRpZmbDqXJlbnRlcyBhbmFseXNlcyBxdWUgbm91cyBhdm9ucyByw6lhbGlzw6llcyBzdXIgY2VzIGRvbm7DqWVzLg0KSWwgZXN0IGNvbXBvc8OpIGRlIHRyb2lzIHBhcnRpZXMgcHJpbmNpcGFsZXMgZGFucyBsZXNxdWVsbGVzIG5vdXMgZXhwbG9yb25zIGxlcyByZWxhdGlvbnMgZW50cmUgbGEgZGl2ZXJzaXTDqSBkZXMgZXNww6hjZXMgZCdvaXNlYXV4LCBsJ2FydGlmaWNpYWxpc2F0aW9uIGRlcyBzb2xzIGV0IGxhIGRpc3RhbmNlIGF2ZWMgbGUgY2VudHJlLXZpbGxlIGRlIEJvcmRlYXV4Lg0KTm91cyBhdm9ucyDDqWdhbGVtZW50IMOpdHVkacOpIGxlcyByw6lnaW1lcyBhbGltZW50YWlyZXMgZGVzIG9pc2VhdXggZXQgbGVzIG1vZGVzIGRlIG5pZGlmaWNhdGlvbiBlbiBmb25jdGlvbiBkZSBjZXJ0YWluZXMgem9uZXMgZ8Opb2dyYXBoaXF1ZXMuDQoNClZvdXMgdHJvdXZlcmV6IGRlcyBjYXJ0ZXMsIGRlcyBncmFwaGlxdWVzLCBkZXMgdGFibGVhdXgsIHVuIHNhbmtpZSBwbG90IGFpbnNpIHF1J3VuIHN1bmJ1cnN0IHBsb3QgcXVpIG5vdXMgcGVybWV0dHJvbnQgZGUgZMOpY291dnJpciBsYSBtZXN1cmUgZGUgbGEgYmlvZGl2ZXJzaXTDqS4NCg0KTCdlbnNlbWJsZSBkZSBjZXMgZG9ubsOpZXMgc29udCB0aXLDqWVzIGR1IHRyYXZhaWwgZGVzIG1lbWJyZXMgZGUgbCdJTlJBRSB1bml0w6kgQmlvR2VDbyBldCBkdSBidXJlYXUgZGUgdMOpbMOpZGV0ZWN0aW9uIEktc2VhLg0KDQpWb3VzIHRyb3V2ZXJleiBkZSBub21icmV1eCByw6lzdWx0YXRzIGNvbW1lIHBsdXNpZXVycyBjb3VyYmVzIGRlIGRpdmVyc2l0w6kgcsOpdsOpbGFudCBsYSBiYWlzc2Ugc2lnbmlmaWNhdGl2ZSBkZSBkaXZlcnNpdMOpIGRhbnMgbGVzIGxpZXV4IGxlcyBwbHVzIGFydGlmaWNpYWxpc8Opcy4NClZvdXMgdHJvdXZlcmV6IMOpZ2FsZW1lbnQgbCdlbnRpw6hyZXTDqSBkZSBub3RyZSBjaGVtaW5lbWVudCBwb3VyIGFycml2ZXIgw6Agbm9zIHLDqXN1bHRhdHMgc3RhdGlzdGlxdWVzLCBub3RhbW1lbnQgY29uY2VybmFudCBsYSBkaXN0cmlidXRpb24gZGVzIGVzcMOoY2VzIGQnb2lzZWF1eCBvYnNlcnbDqWVzIGVuIEdpcm9uZGUsIG1haXMgYXVzc2kgZGVzIHN0YXRpc3RpcXVlcyBtdWx0aXZhcmnDqWVzIGV0IGRlc2NyaXB0aXZlcy4NCg0KTm91cyB2b3VzIGludml0b25zIMOgIGTDqWNvdXZyaXIgbCdlbnNlbWJsZSBkZSBub3MgYW5hbHlzZXMgZXQgw6AgZXhwbG9yZXIgbGVzIGRpZmbDqXJlbnRlcyB2aXN1YWxpc2F0aW9ucyBxdWUgbm91cyBhdm9ucyByw6lhbGlzw6llcyBwb3VyIG1pZXV4IGNvbXByZW5kcmUgbGVzIHJlbGF0aW9ucyBlbnRyZSBsZXMgZGlmZsOpcmVudGVzIHZhcmlhYmxlcyBkZSBub3RyZSBqZXUgZGUgZG9ubsOpZXMuDQpUb3V0ZXMgbm9zIHZpc3VhbGlzYXRpb25zIHNvbnQgaW50ZXJhY3RpdmVzIGV0IHZvdXMgcGVybWV0dGVudCBkZSB6b29tZXIsIGRlIGTDqXBsYWNlciBldCBkZSBjbGlxdWVyIHN1ciBsZXMgZGlmZsOpcmVudHMgw6lsw6ltZW50cyBwb3VyIG9idGVuaXIgcGx1cyBkJ2luZm9ybWF0aW9ucy4NCkNlbGEgdm91cyBwZXJtZXR0cmEgZCdhdm9pciB1bmUgZXhww6lyaWVuY2UgcGx1cyBpbW1lcnNpdmUgZXQgdm91cyBwZXJtZXR0cmEgZGUgY3LDqWVyIHZvcyBwcm9wcmVzIGFuYWx5c2VzLCBlbiBwYXJhbGzDqGxlIMOgIGNlbGxlcyByw6lhbGlzw6llcyBwYXIgbm9zIHNvaW5zLg0KDQpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgUGFja2FnZXMgZGUgZ2VzdGlvbiBkZXMgZG9ubsOpZXMNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCg0KIyBQYWNrYWdlcyBwb3VyIHZpc3VhbGlzYXRpb24gZGVzIGRvbm7DqWVzDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdnc3BhdGlhbCkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkobGF0dGljZSkNCmxpYnJhcnkocGxvdGx5KQ0KDQojIFBhY2thZ2VzIHBvdXIgdmlzdWFsaXNhdGlvbiBkZSBjYXJ0ZXMgaW50w6lyYWN0aXZlcw0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShsZWFmcG9wKQ0KbGlicmFyeShsZWFmbGV0LmV4dHJhcykNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG91dC53aWR0aCA9ICIxMDAlIiwgZWNobyA9IFRSVUUpDQpgYGANCg0KIyBFeHBsb3JhdGlvbiBkZXMgZG9ubsOpZXMNCg0KUG91ciBjb21tZW5jZXIsIG5vdXMgY2hhcmdlb25zIGxlcyBkb25uw6llcyBldCBub3VzIGxlcyBleHBsb3JvbnMgcG91ciBtaWV1eCBjb21wcmVuZHJlIGxldXIgc3RydWN0dXJlLg0KRGFucyB1biBwcmVtaWVyIHRlbXBzLCBub3VzIGFsbG9ucyBham91dGVyIHVuZSBjb2xvbm5lIMOgIG5vdHJlIERhdGFGcmFtZSBxdWkgY29udGllbnQgbGVzIG5vbXMgbGF0aW5zIGRlcyBvaXNlYXV4IGFmaW4gZGUgZmFjaWxsaXRlciBsZXMgYW5hbHlzZXMgcGFyIGxhIHN1aXRlLg0KYGBge3J9DQpvaXNlYXV4IDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL09pc2VhdXhfdXBfdG9fMjAyMy5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiKQ0KIyBDUsOJRVIgVU5FIE5PVVZFTExFIENPTE9OTkUgREFOUyBMRSBEQVRBRlJBTUUgT0lTRUFVWCBRVUkgQ09OVElFTlQgTEUgTk9NIExBVElOIERFIEwnT0lTRUFVDQpteV9zcGxpdCA8LSBmdW5jdGlvbihhcnJheSwgc3RyID0gIiBcXHwgIikgew0KICBvdXQgPC0gcmVwKE5BLCBsZW5ndGgoYXJyYXkpKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoYXJyYXkpKSB7DQogICAgb3V0W2ldIDwtIHVubGlzdChzdHJzcGxpdChhcnJheVtpXSwgc3RyKSlbMV0NCiAgfQ0KICByZXR1cm4ob3V0KQ0KfQ0KDQpvbmx5X2xhdGluIDwtIG15X3NwbGl0KGFzLnZlY3RvcihvaXNlYXV4JE5vbV9UYXhvbl9DaXRlKSkNCg0Kb2lzZWF1eCRsYXRpbiA8LSBvbmx5X2xhdGluDQpvaXNlYXV4JGFubmVlIDwtIGFzLm51bWVyaWMoc3Vic3RyKG9pc2VhdXgkRGF0ZSwgMSwgNCkpDQpgYGANCg0KDQpQYXIgbGEgc3VpdGUsIG5vdXMgYWxsb25zIGV4cGxvcmVyIGxlcyBkb25uw6llcyBwb3VyIG1pZXV4IGNvbXByZW5kcmUgbGEgZGlzdHJpYnV0aW9uIGRlcyBlc3DDqGNlcyBkJ29pc2VhdXggb2JzZXJ2w6llcy4NClZvaWNpIHVuIHRhYmxlYXUgcXVpIG1vbnRyZSBsZXMgMTAgZXNww6hjZXMgZCdvaXNlYXV4IGxlcyBwbHVzIGZyw6lxdWVtbWVudCBvYnNlcnbDqWVzIGRhbnMgbCdlbnNlbWJsZSBkZXMgZG9ubsOpZXMuDQpgYGB7cn0NCiMgTEVTIEVTUMOIQ0VTIEQnT0lTRUFVWCBMRVMgUExVUyBGUsOJUVVFTlRFUyBPQlNFUlbDiUVTIERBTlMgTCdFTlNFTUJMRSBERVMgRE9OTsOJRVMNCmFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShvaXNlYXV4JGxhdGluKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdKQ0KYGBgDQoNCk5vdXMgYWxsb25zIG1haW50ZW5hbnQgZXhwbG9yZXIgbGEgZnLDqXF1ZW5jZSBkZXMgZXNww6hjZXMgZCdvaXNlYXV4IG9ic2VydsOpZXMgZGFucyBsJ2Vuc2VtYmxlIGRlcyBkb25uw6llcyBwYXIgYW5uw6llLg0KVm9pY2ksIHBhciBvcmRyZSBhbHBoYWLDqXRpcXVlLCBsZSB0YWJsZWF1IG1ldHRhbnQgZW4gw6l2aWRlbmNlIGNldHRlIGZyw6lxdWVuY2UuDQoNCmBgYHtyfQ0KIyBGUsOJUVVFTkNFIERFUyBFU1DDiENFUyBEJ09JU0VBVVggT0JTRVJWw4lFUyBEQU5TIEwnRU5TRU1CTEUgREVTIERPTk7DiUVTIFBBUiBBTk7DiUUNCkFubmVlIDwtIG15X3NwbGl0KGFzLnZlY3RvcihvaXNlYXV4JERhdGUpLCBzdHIgPSAiLSIpDQpvaXNlYXV4JEFubmVlIDwtIGFzLmZhY3RvcihBbm5lZSkNCmFzLmRhdGEuZnJhbWUubWF0cml4KHRhYmxlKG9pc2VhdXgkbGF0aW4sIG9pc2VhdXgkQW5uZWUpKQ0KYGBgDQoNCiMjIyBNZXN1cmUgZGUgbGEgZGl2ZXJzaXTDqSB7LnRhYnNldH0NCg0KRGFucyBjZXR0ZSBwYXJ0aWUsIG5vdXMgYWxsb25zIGV4cGxvcmVyIGxhIG1lc3VyZSBkZSBsYSBkaXZlcnNpdMOpIMOgIMOgIGwnYWlkZSBkZSBwbHVzaWV1cnMgZW50cm9waWVzLg0KTm91cyBub3VzIGludMOpcmVzc2Vyb25zIHVuaXF1ZW1lbnQgYXUgTU9TMTEsIGMnZXN0IMOgIGRpcmUgbGVzIHN1cmZhY2VzIGFydGlmaWNpYWxpc8OpZXMuIE9uIHByZW5kIGNvbW1lIGJ1ZmZlciBzaXplIDUwMG0uDQoNCkRhbnMgdW4gcHJlbWllciB0ZW1wcywgbm91cyBhbGxvbnMgdXRpbGlzZXIgbGEgcHJvcG9ydGlvbiBk4oCZZXNww6hjZXMgZGlmZsOpcmVudGVzIG9ic2VydsOpZXMgZGFucyB1bmUgc3RhdGlvbiBwb3VyIG1lc3VyZXIgbGEgZGl2ZXJzaXTDqS4gRGFucyB1biBkZXV4acOobWUgdGVtcHMsIG5vdXMgYWxsb25zIHV0aWxpc2VyIGzigJllbnRyb3BpZSBkZSBTaGFubm9uIGV0IGVuZmluIGwnaW5kaWNlIGRlIFNpbXBzb24uDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMgQW5hbHlzZSBkZSBkaXZlcnNpdMOpIHBhciByYXBwb3J0IMOgIE1PUzExIGV0IHBhciBhbm7DqWUNCg0KZGVub21icmVtZW50IDwtIG9pc2VhdXggJT4lDQogIGdyb3VwX2J5KENvZGVfTWFpbGxlLCBhbm5lZSwgbGF0aW4pICU+JQ0KICBzdW1tYXJpc2Uoc3VtID0gc3VtKERlbm9tYnJlbWVudF9taW4sIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBhcnJhbmdlKGRlc2MoQ29kZV9NYWlsbGUpKQ0KDQpkZW5vbWJyZW1lbnQkcCA8LSByZXAoTkEsIG5yb3coZGVub21icmVtZW50KSkNCmZvciAoaSBpbiAxOm5yb3coZGVub21icmVtZW50KSkgew0KICBudW1lcmF0b3IgPC0gZGVub21icmVtZW50JHN1bVtpXQ0KICBkZW5vbWluYXRvciA8LQ0KICAgIHN1bShkZW5vbWJyZW1lbnQkc3VtW3doaWNoKGRlbm9tYnJlbWVudCRDb2RlX01haWxsZSA9PSBkZW5vbWJyZW1lbnQkQ29kZV9NYWlsbGVbaV0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmIGRlbm9tYnJlbWVudCRhbm5lZSA9PSBkZW5vbWJyZW1lbnQkYW5uZWVbaV0pXSkNCiAgZGVub21icmVtZW50JHBbaV0gPC0gbnVtZXJhdG9yIC8gZGVub21pbmF0b3INCn0NCg0KaW5kZXggPC0gZGVub21icmVtZW50ICU+JQ0KICBncm91cF9ieShDb2RlX01haWxsZSwgYW5uZWUgPSBmYWN0b3IoYW5uZWUpKSAlPiUNCiAgc3VtbWFyaXNlKEQxID0gc3VtKHAgPiAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgRDIgPSBleHAoLXN1bShwICogbG9nKHApKSksDQogICAgICAgICAgICBEMyA9IDEgLyBzdW0ocF4yKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyhDb2RlX01haWxsZSkpDQoNCkxVUCA8LSByZWFkLmNzdigiZGF0YS9iaXJkcy9MYW5kVXNlUGVyX0JNXzIwMjNfY2FydG9JU2VhLmNzdiIsIGhlYWRlciA9IFRSVUUpDQppbmRleCRNT1MxMSA8LSByZXAoTkEsIG5yb3coaW5kZXgpKQ0KZm9yIChpIGluIDE6bnJvdyhpbmRleCkpIHsNCiAgaW5kZXgkTU9TMTFbaV0gPC0gTFVQJE1PUzExW3doaWNoKGluZGV4JENvZGVfTWFpbGxlW2ldID09IExVUCRJRCAmIExVUCRCdWZmZXJTaXplID09IDUwMCldDQp9DQpgYGANCg0KIyMjIyBQcm9wb3J0aW9uIGQnZXNww6hjZXMNCg0KVm9pY2kgbGUgZ3JhcGhpcXVlIHF1aSBtb250cmUgbGEgcmVsYXRpb24gZW50cmUgbGEgZGl2ZXJzaXTDqSBldCBNT1MxMSBwb3VyIGNoYXF1ZSBhbm7DqWUgZXhwbGlxdcOpZSBwYXIgbGEgcHJvcG9ydGlvbiBkJ2VzcMOoY2VzLg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZ2dwbG90KGluZGV4LCBhZXMoeCA9IE1PUzExLCB5ID0gRDEsIGNvbG9yID0gYXMuZmFjdG9yKGFubmVlKSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIsIHNlID0gVFJVRSwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMikgKw0KICBsYWJzKHRpdGxlID0gIlByb3BvcnRpb24gZOKAmWVzcMOoY2VzIGVuIGZvbmN0aW9uIGRlIE1PUzExIiwNCiAgICAgICB4ID0gIk1PUzExIiwNCiAgICAgICB5ID0gIlByb3BvcnRpb24gZCdlc3DDqGNlcyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyhjb2xvdXIgPSAiQW5uw6llIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQojIyMjIEluZGljZSBkZSBTaGFub24NCg0KQ2VjaSBlc3QgbGUgZ3JhcGhpcXVlIHF1aSBtb250cmUgbGEgcmVsYXRpb24gZW50cmUgbGEgZGl2ZXJzaXTDqSBldCBNT1MxMSBwb3VyIGNoYXF1ZSBhbm7DqWUgZXhwbGlxdcOpZSBwYXIgbCdlbnRyb3BpZSBkZSBTaGFubm9uLg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZ2dwbG90KGluZGV4LCBhZXMoeCA9IE1PUzExLCB5ID0gRDIsIGNvbG9yID0gYXMuZmFjdG9yKGFubmVlKSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIsIHNlID0gVFJVRSwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMikgKw0KICBsYWJzKHRpdGxlID0gIkluZGljZSBkZSBTaGFub24gZW4gZm9uY3Rpb24gZGUgTU9TMTEiLA0KICAgICAgIHggPSAiTU9TMTEiLA0KICAgICAgIHkgPSAiSW5kaWNlIGRlIFNoYW5vbiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyhjb2xvdXIgPSAiQW5uw6llIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQojIyMjIEluZGljZSBkZSBTaW1wc29uDQoNCkNlY2kgZXN0IGxlIGdyYXBoaXF1ZSBxdWkgbW9udHJlIGxhIHJlbGF0aW9uIGVudHJlIGxhIGRpdmVyc2l0w6kgZXQgTU9TMTEgcG91ciBjaGFxdWUgYW5uw6llIGV4cGxpcXXDqWUgcGFyIGwnaW5kaWNlIGRlIFNpbXBzb24uDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0V9DQpnZ3Bsb3QoaW5kZXgsIGFlcyh4ID0gTU9TMTEsIHkgPSBEMywgY29sb3IgPSBhcy5mYWN0b3IoYW5uZWUpKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4yKSArDQogIGxhYnModGl0bGUgPSAiSW5kaWNlIGRlIFNpbXBzb24gZW4gZm9uY3Rpb24gZGUgTU9TMTEiLA0KICAgICAgIHggPSAiTU9TMTEiLA0KICAgICAgIHkgPSAiSW5kaWNlIGRlIFNpbXBzb24iKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoY29sb3VyID0gIkFubsOpZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpgYGANCg0KIyMjDQoNCkxhIHRlbmRhbmNlIGdsb2JhbGUgZXN0IGwnYXVnbWVudGF0aW9uIGRlIGxhIGRpdmVyc2l0w6kgYXUgZMOpYnV0IGRlIGxhIGNvdXJiZSBqdXNxdSfDoCBhdHRlaW5kcmUgdW4gbWF4aW11bSBwdWlzIHVuZSBiYWlzc2UgcXVhbmQgbGUgTU9TMTEgYXVnbWVudGUgZGF2YW50YWdlLiBDJ2VzdC3DoC1kaXJlIHF1ZSwgbW9pbnMgbGVzIHNvbHMgc29udCBhcnRpZmljaWFsaXPDqXMsIHBsdXMgbGEgZGl2ZXJzaXTDqSBlc3QgZ3JhbmRlIGF2ZWMgdW5lIGRpdmVyc2l0w6kgbWF4aW1hbGUgYXR0ZWludGUgcXVhbmQgbGUgbWlsaWV1IGVzdCDDoCBsYSBmb2lzIGFydGlmaWNpYWxpc8OpIG1haXMgcHLDqXNlbnRlIMOpZ2FsZW1lbnQgZGVzIHN1cmZhY2VzIG5vbiBhcnRpZmljaWFsaXPDqWVzLiANCg0KDQpOb3RyZSBkZXV4acOobWUgcGFydGllIHNlIHBvcnRlcmEgc3VyIGwnYW5hbHlzZSBkZSBsYSBkaXZlcnNpdMOpIGRlcyBlc3DDqGNlcyBkJ29pc2VhdXggZW4gZm9uY3Rpb24gZGUgbGEgZGlzdGFuY2UgYXZlYyBsZSBjZW50cmUtdmlsbGUgZGUgQm9yZGVhdXguDQpPbiBvYnNlcnZlcmEgdW5lIHRlbmRhbmNlIGRlIGRpdmVyc2l0w6kgcGFyIHJhcHBvcnQgw6AgbGEgZGlzdGFuY2UgZGUgbGEgbWFpbGxlIGF2ZWMgbGUgY2VudHJlIHZpbGxlIHF1aSBkaWZmw6hyZSBsw6lnw6hyZW1lbnQgcGFyIHJhcHBvcnQgYXV4IHLDqXN1bHRhdHMgY2ktZGVzc3VzLg0KUG91ciBjZSBmYWlyZSwgbm91cyBhbGxvbnMgY2FsY3VsZXIgdG91dGVzIGxlcyBkaXN0YW5jZXMgZGUgY2hhcXVlIG1haWxsZSDDoCBQZXlCZXJsYW5kLCBxdWkgc2VyYSBub3RyZSBwb2ludCByw6lmw6lyZW50IHBvdXIgbGUgY2VudHJlLiANCg0KIyBUZW5kYW5jZSBkZSBkaXZlcnNpdMOpIHBhciByYXBwb3J0IMOgIGxhIGRpc3RhbmNlIGF2ZWMgbGUgY2VudHJlLXZpbGxlDQoNCmBgYHtyfQ0KUGV5QmVybGFuZCA8LSBkYXRhLmZyYW1lKCJMYXRpdHVkZSIgPSA0NC44MzgxNjgsICJMb25naXR1ZGUiID0gLTAuNTc4ODAzKQ0KDQojIE9uIGNvbnZlcnRpdCBsZXMgY29vcmRvbm7DqWVzIGRlIFBleUJlcmxhbmQgZW4gc2YNCg0KUGV5QmVybGFuZCA8LSBzdF9hc19zZihQZXlCZXJsYW5kLCBjb29yZHMgPSBjKCJMb25naXR1ZGUiLCAiTGF0aXR1ZGUiKSwgY3JzID0gNDMyNikNCg0KIyBPbiB2YSBjcsOpZXIgdW4gZGF0YWZyYW1lIHF1aSBjb250aWVudCBsZXMgY29vcmRvbm7DqWVzIGRlIHRvdXRlcyBsZXMgc3RhdGlvbnMgY29kZV9tYWlsbGUNCg0KY29vcmRpbmF0ZXMgPC0gc3RfYXNfc2YoTFVQLCBjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gMjE1NCkNCg0KIyBPbiB2YSB0cmFuc2Zvcm1lciBsZXMgY29vcmRvbm7DqWVzIGRlIDIxNTQgw6AgNDMyNg0KDQpjb29yZGluYXRlcyA8LSBzdF90cmFuc2Zvcm0oY29vcmRpbmF0ZXMsIGNycyA9IDQzMjYpDQoNCiMgT24gdmEgY2FsY3VsZXIgbGVzIGRpc3RhbmNlcyBlbnRyZSBsZXMgc3RhdGlvbnMgZXQgbGUgY2VudHJlIHZpbGxlIFBleUJlcmxhbmQNCg0KY29vcmRpbmF0ZXMkRGlzdGFuY2UgPC0gc3RfZGlzdGFuY2UoY29vcmRpbmF0ZXMsIFBleUJlcmxhbmQpDQpgYGANCg0KT24gYWpvdXRlIGxlcyBkaXN0YW5jZXMgw6Agbm9zIGRvbm7DqWVzIGRlIGRpdmVyc2l0w6kuIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmluZGV4JERpc3RhbmNlIDwtIHJlcChOQSwgbnJvdyhpbmRleCkpDQpmb3IgKGkgaW4gMTpucm93KGluZGV4KSkgew0KICBpbmRleCREaXN0YW5jZVtpXSA8LSBjb29yZGluYXRlcyREaXN0YW5jZVt3aGljaChpbmRleCRDb2RlX01haWxsZVtpXSA9PSBjb29yZGluYXRlcyRJRCAmIGNvb3JkaW5hdGVzJEJ1ZmZlclNpemUgPT0gNTAwKV0NCn0NCg0KcGFyKG1mcm93ID0gYygxLCAzKSkNCmBgYA0KDQpQb3VyIGZhaXJlIGwnYW5hbHlzZSwgb24gdXRpbGlzZSBsZXMgdHJvaXMgbWVzdXJlcyBkZSBkaXZlcnNpdMOpIHF1ZSBub3VzIGF2b25zIHV0aWxpc8OpZXMgcHLDqWPDqWRlbW1lbnQuDQoNCiMjIyBNZXN1cmUgZGUgbGEgZGl2ZXJzaXTDqSB7LnRhYnNldH0NCg0KIyMjIyBQcm9wb3J0aW9uIGQnZXNww6hjZXMNCg0KVm9pY2kgbGEgY291cmJlIGRlIGRpdmVyc2l0w6kgZXhwbGlxdcOpZSBwYXIgbGEgcHJvcG9ydGlvbiBkJ2VzcMOoY2VzLg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZ2dwbG90KGluZGV4LCBhZXMoeCA9IERpc3RhbmNlLCB5ID0gRDEsIGNvbG9yID0gYXMuZmFjdG9yKGFubmVlKSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIsIHNlID0gVFJVRSwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMikgKw0KICBsYWJzKHRpdGxlID0gIlByb3BvcnRpb24gZCdlc3DDqGNlcyBlbiBmb25jdGlvbiBkZSBsYSBkaXN0YW5jZSBhdSBjZW50cmUtdmlsbGUiLA0KICAgICAgIHggPSAiRGlzdGFuY2UiLA0KICAgICAgIHkgPSAiUHJvcG9ydGlvbiBkJ2VzcMOoY2VzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKGNvbG91ciA9ICJBbm7DqWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCiMjIyMgSW5kaWNlIGRlIFNoYW5vbg0KDQpWb2ljaSBsYSBjb3VyYmUgZGUgZGl2ZXJzaXTDqSBleHBsaXF1w6llIHBhciBsJ2VudHJvcGllIGRlIFNoYW5ub24uDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0V9DQpnZ3Bsb3QoaW5kZXgsIGFlcyh4ID0gRGlzdGFuY2UsIHkgPSBEMiwgY29sb3IgPSBhcy5mYWN0b3IoYW5uZWUpKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4yKSArDQogIGxhYnModGl0bGUgPSAiSW5kaWNlIGRlIFNoYW5vbiBlbiBmb25jdGlvbiBkZSBsYSBkaXN0YW5jZSBhdSBjZW50cmUtdmlsbGUiLA0KICAgICAgIHggPSAiRGlzdGFuY2UiLA0KICAgICAgIHkgPSAiSW5kaWNlIGRlIFNoYW5vbiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyhjb2xvdXIgPSAiQW5uw6llIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQojIyMjIEluZGljZSBkZSBTaW1wc29uDQoNClZvaWNpIGxhIGNvdXJiZSBkZSBkaXZlcnNpdMOpIGV4cGxpcXXDqWUgcGFyIGwnaW5kaWNlIGRlIFNpbXBzb24uDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0V9DQpnZ3Bsb3QoaW5kZXgsIGFlcyh4ID0gRGlzdGFuY2UsIHkgPSBEMywgY29sb3IgPSBhcy5mYWN0b3IoYW5uZWUpKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4yKSArDQogIGxhYnModGl0bGUgPSAiSW5kaWNlIGRlIFNpbXBzb24gZW4gZm9uY3Rpb24gZGUgbGEgZGlzdGFuY2UgYXUgY2VudHJlLXZpbGxlIiwNCiAgICAgICB4ID0gIkRpc3RhbmNlIiwNCiAgICAgICB5ID0gIkluZGljZSBkZSBTaW1wc29uIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKGNvbG91ciA9ICJBbm7DqWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCiMjIw0KDQpMYSB0ZW5kYW5jZSBnbG9iYWxlIGVzdCBsJ2F1Z21lbnRhdGlvbiBkZSBsYSBkaXZlcnNpdMOpIGF1IGTDqWJ1dCBkZSBsYSBjb3VyYmUganVzcXUnw6AgYXR0ZWluZHJlIHVuIG1heGltdW0gcHVpcyB1bmUgYmFpc3NlIHF1YW5kIGxhIGRpc3RhbmNlIGF2ZWMgbGUgY2VudHJlLXZpbGxlIGF1Z21lbnRlIGRhdmFudGFnZS4gTm91cyBwb3V2b25zIHZvaXIgcXVlIHBvdXIgbm9zIHRyb2lzIG1lc3VyZXMgZGUgZGl2ZXJzaXTDqSwgbGUgcGljIGVzdCBhdHRlaW50IMOgIGVudmlyb24gOWttLiBMYSBkaXZlcnNpdMOpIGVzdCBkb25jIHBsdXMgZ3JhbmRlIGxvcnNxdWUgbGEgbWFpbGxlIHNlIHRyb3V2ZXIgw6AgZW52aXJvbiA5a20gZHUgY2VudHJlLXZpbGxlLg0KTm91cyByZXRyb3V2b25zIGRvbmMgbGEgbcOqbWUgb2JzZXJ2YXRpb24gcXUnYXZlYyBsYSB2YXJpYWJsZSBkJ2FydGlmaWNpYWxpc2F0aW9uIGRlcyBzb2xzIChNT1NTMTEpLCBjYXIgbCdhcnRpZmljaWFsaXNhdGlvbiBkZXMgc29scyBldCBsYSBkaXN0YW5jZSBhdmVjIGxlIGNlbnRyZSB2aWxsZSBzb250IHBvc2l0aXZlbWVudCBjb3Jyw6lsw6llcyAocGV1dC1ldHJlIGVzc2F5ZXIgZGUgbWVzdXJlciBsYSBjb3JyZWxhdGlvbiBlbnRyZSBjZXMgZGV1eCB2YXJpYWJsZXMgc3VyIG5vcyBkb25uw6llcykNCg0KDQpQYXIgbGEgc3VpdGUsIGwnb2JqZWN0aWYgdmEgw6p0cmUgZGUgY29tcHJlbmRyZSBwb3VycXVvaSBjZSBwaWMgZGUgZGl2ZXJzaXTDqSBkZXMgZXNww6hjZXMgZXN0IG9ic2VydsOpIMOgIGNlIHBvdXJjZW50YWdlIGQnYXJ0aWZpY2lhbGlzYXRpb24uIFBvdXIgY2UgZmFpcmUsIG5vdXMgYWxsb25zIGludHJvZHVpcmUgdW4gbm91dmVhdSBqZXUgZGUgZG9ubsOpZXMgc3VyIGxlcyBjYXJhY3TDqXJpc3RpcXVlcyBkZXMgZXNww6hjZXMgZCdvaXNlYXV4Lg0KTm91cyBhbGxvbnMgY29tYmluZXIgY2UgamV1IGRlIGRvbm7DqWVzIGF2ZWMgbGVzIGF1dHJlcyBqZXV4IGRlIGRvbm7DqWVzIGFmaW4gZCdlbiB0aXJlciBkZXMgYW5hbHlzZXMsIHByaW5jaXBhbGVtZW50IGdyw6JjZSDDoCBkZXMgY2FydGVzIGludGVyYWN0aXZlcy4gDQoNCg0KYGBge3J9DQpvaXNlYXV4IDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL09pc2VhdXhfdXBfdG9fMjAyMy5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiKQ0KTFVQIDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL0xhbmRVc2VQZXJfQk1fMjAyM19jYXJ0b0lTZWEuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmBgYA0KDQpgYGB7cn0NCiMgQ1LDiUVSIFVORSBOT1VWRUxMRSBDT0xPTk5FIERBTlMgTEUgREFUQUZSQU1FIE9JU0VBVVggUVVJIENPTlRJRU5UIExFIE5PTSBMQVRJTiBERSBMJ09JU0VBVQ0KIyBMRSBOT00gTEFUSU4gRVNUIExFIFBSRU1JRVIgTk9NIERFIExBIENPTE9OTkUgIk5vbV9UYXhvbl9DaXRlDQojIFNJIExFIE5PTSBDT05USUVOVCBVTiAifCIsIExFIE5PTSBMQVRJTiBFU1QgTEUgUFJFTUlFUiBOT00gQVZBTlQgTEUgInwiDQoNCiMgRGl2aXNlciBsZXMgbm9tcw0KbXlfc3BsaXQgPC0gZnVuY3Rpb24oYXJyYXksIHN0ciA9ICIgXFx8ICIpIHsNCiAgb3V0IDwtIHJlcChOQSwgbGVuZ3RoKGFycmF5KSkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGFycmF5KSkgew0KICAgIG91dFtpXSA8LSB1bmxpc3Qoc3Ryc3BsaXQoYXJyYXlbaV0sIHN0cikpWzFdDQogIH0NCiAgcmV0dXJuKG91dCkNCn0NCg0KIyBUZXN0ZXIgbGEgZm9uY3Rpb24NCm9ubHlfbGF0aW4gPC0gbXlfc3BsaXQoYXMudmVjdG9yKG9pc2VhdXgkTm9tX1RheG9uX0NpdGUpKQ0KDQojIE9uIGFqb3V0ZSBsYSBub3V2ZWxsZSBjb2xvbm5lIGF1IGRhdGFmcmFtZQ0Kb2lzZWF1eCRsYXRpbiA8LSBvbmx5X2xhdGluDQpgYGANCg0KVm9pY2kgbGUgdGFibGVhdSBxdWkgcmVwcsOpc2VudGUgbGUgTU9TMTEgcG91ciBjaGFxdWUgc3RhdGlvbiBkZSBtZXN1cmUuDQpDZXR0ZSBtZXN1cmUgbm91cyBpbmRpcXVlIMOgIHF1ZWwgcG9pbnQgbGUgc29sIGVzdCBhcnRpZmljaWFsaXPDqS4NCmBgYHtyfQ0KIyBDUsOJRVIgVU5FIE5PVVZFTExFIENPTE9OTkUgREFOUyBMRSBEQVRBRlJBTUUgT0lTRUFVWCBRVUkgQ09OVElFTlQgTEEgVkFMRVVSIE1PUzExIERVIFBPSU5UDQpmaWx0ZXIgPC0gTFVQJEJ1ZmZlclNpemUgPT0gNTAwDQpMVVBfNTAwX01PUzExIDwtIExVUFtmaWx0ZXIsIGMoIkdlb21ldHJ5IiwgIklEIiwgIlgiLCAiWSIsICJCdWZmZXJTaXplIiwgIk1PUzExIildDQpyb3duYW1lcyhMVVBfNTAwX01PUzExKSA8LSAxOm5yb3coTFVQXzUwMF9NT1MxMSkNCkxVUF81MDBfTU9TMTFbLCBjKCJJRCIsICJNT1MxMSIpXQ0KDQpNT1MxMSA8LSByZXAoTkEsIG5yb3cob2lzZWF1eCkpDQpmb3IgKGkgaW4gMTpucm93KG9pc2VhdXgpKSB7DQogIE1PUzExW2ldIDwtIHdoaWNoKG9pc2VhdXgkQ29kZV9NYWlsbGVbaV0gPT0gTFVQXzUwMF9NT1MxMSRJRCkNCn0NCg0KIyBBam91dGVyIGxhIGNvbG9ubmUgTU9TMTEgYXUgZGF0YWZyYW1lDQpvaXNlYXV4JE1PUzExIDwtIExVUF81MDBfTU9TMTEkTU9TMTFbTU9TMTFdDQpgYGANCg0KIyBBbmFseXNlcyBpbnTDqXJhY3RpdmVzIGV0IGfDqW9ncmFwaGlxdWVzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpvaXNlYXV4IDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL09pc2VhdXhfdXBfdG9fMjAyMy5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiKQ0KIyBDUkVBVEUgQSBORVcgQ09MVU1OIElOIFRIRSBPSVNFQVVYIERBVEFGUkFNRSBUSEFUIENPTlRBSU5TIFRIRSBMQVRJTiBOQU1FIE9GIFRIRSBCSVJEDQpteV9zcGxpdCA8LSBmdW5jdGlvbihhcnJheSwgc3RyID0gIiBcXHwgIikgew0KICBvdXQgPC0gcmVwKE5BLCBsZW5ndGgoYXJyYXkpKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoYXJyYXkpKSB7DQogICAgb3V0W2ldIDwtIHVubGlzdChzdHJzcGxpdChhcnJheVtpXSwgc3RyKSlbMV0NCiAgfQ0KICByZXR1cm4gKG91dCkNCn0NCg0Kb25seV9sYXRpbiA8LSBteV9zcGxpdChhcy52ZWN0b3Iob2lzZWF1eCROb21fVGF4b25fQ2l0ZSkpDQojIGxlbmd0aCh1bmlxdWUob25seV9sYXRpbikpID09IGxlbmd0aCh1bmlxdWUob2lzZWF1eCRDb2RlX1JlZikpDQoNCm9pc2VhdXgkbGF0aW4gPC0gb25seV9sYXRpbg0Kb2lzZWF1eCRhbm5lZSA8LSBhcy5udW1lcmljKHN1YnN0cihvaXNlYXV4JERhdGUsIDEsIDQpKQ0KDQpkZW5vbWJyZW1lbnQgPC0gb2lzZWF1eCAlPiUNCiAgZ3JvdXBfYnkoQ29kZV9NYWlsbGUsIGFubmVlLCBsYXRpbikgJT4lDQogIHN1bW1hcmlzZShzdW0gPSBzdW0oRGVub21icmVtZW50X21pbiwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGFycmFuZ2UoZGVzYyhDb2RlX01haWxsZSkpDQoNCmRlbm9tYnJlbWVudCRwIDwtIHJlcChOQSwgbnJvdyhkZW5vbWJyZW1lbnQpKQ0KZm9yIChpIGluIDE6bnJvdyhkZW5vbWJyZW1lbnQpKSB7DQogIG51bWVyYXRvciA8LSBkZW5vbWJyZW1lbnQkc3VtW2ldDQogIGRlbm9taW5hdG9yIDwtIA0KICAgIHN1bShkZW5vbWJyZW1lbnQkc3VtW3doaWNoKGRlbm9tYnJlbWVudCRDb2RlX01haWxsZSA9PSBkZW5vbWJyZW1lbnQkQ29kZV9NYWlsbGVbaV0gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJiBkZW5vbWJyZW1lbnQkYW5uZWUgPT0gZGVub21icmVtZW50JGFubmVlW2ldKV0pDQogIGRlbm9tYnJlbWVudCRwW2ldIDwtIG51bWVyYXRvciAvIGRlbm9taW5hdG9yDQp9DQoNCmluZGV4IDwtIGRlbm9tYnJlbWVudCAlPiUNCiAgZ3JvdXBfYnkoQ29kZV9NYWlsbGUsIGFubmVlID0gZmFjdG9yKGFubmVlKSkgJT4lDQogIHN1bW1hcmlzZShEMSA9IHN1bShwID4gMCwgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICBEMiA9IGV4cCgtc3VtKHAqbG9nKHApKSksIA0KICAgICAgICAgICAgRDMgPSAxIC8gc3VtKHBeMiksIC5ncm91cHMgPSAnZHJvcCcpICU+JQ0KICBhcnJhbmdlKGRlc2MoQ29kZV9NYWlsbGUpKQ0KDQpMVVAgPC0gcmVhZC5jc3YoImRhdGEvYmlyZHMvTGFuZFVzZVBlcl9CTV8yMDIzX2NhcnRvSVNlYS5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KaW5kZXgkTU9TMTEgPC0gcmVwKE5BLCBucm93KGluZGV4KSkNCmZvciAoaSBpbiAxOm5yb3coaW5kZXgpKSB7DQogIGluZGV4JE1PUzExW2ldIDwtIExVUCRNT1MxMVt3aGljaChpbmRleCRDb2RlX01haWxsZVtpXSA9PSBMVVAkSUQgJiBMVVAkQnVmZmVyU2l6ZSA9PSAxMDAwKV0NCn0NCg0KDQp0cmFpdHMgPC0gcmVhZC5jc3YoImRhdGEvYmlyZHMvdHJhaXRzLXN0YXR1dC1JVUNOLWJpb2RpdmVyY2l0ZS5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KY2l0ZSA8LSByZWFkLmNzdigiZGF0YS9iaXJkcy9CaW9kaXZlckNpdGVfc2l0ZXMuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwPSI7IikNCg0KI29uIHZldXQgdHJhaXRlciBsYSB0YWJsZSBkw6lub21icmVtZW50IHF1J29uIGFwcGVsZXJhIGRlbm9tYnJlbWVudENhcnRlIG/DuSBpbCByZXN0ZXJhIHBvdXIgY2hhcXVlIG1haWxsZSBldCBwb3VyIGNoYXF1ZSBhbm7DqWUgbGUgdG9wIDMgZGVzIG9pc2VhdXggbGVzIHBsdXMgb2JzZXJ2w6lzIHNhbnMgbGEgdmFyaWFibGUgcA0KDQpkZW5vbWJyZW1lbnRDYXJ0ZSA8LSBkZW5vbWJyZW1lbnQgJT4lDQogIGdyb3VwX2J5KENvZGVfTWFpbGxlLCBhbm5lZSkgJT4lDQogIHRvcF9uKDMsIHN1bSkgJT4lDQogIGFycmFuZ2UoQ29kZV9NYWlsbGUsIGFubmVlLCBkZXNjKHN1bSkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIHNlbGVjdCgtcCkNCg0KDQojb24gdmV1dCBjaGFuZ2VyIGNldHRlIHRhYmxlIHBvdXIgZmFpcmUgYXBwYXJhaXRyZSBkZSBub3V2ZWxsZXMgY29sb25uZXMgcG91ciBxdSdpbCB5IGFpdCA6IHVuZSBsaWduZSBwYXIgbWFpbGxlIGV0IHBhciBhbm7DqWUsIGV0IHBvdXIgY2hhcXVlIG9pc2VhdSwgbGUgbm9tYnJlIGQnaW5kaXZpZHVzIG9ic2VydsOpcw0KDQpkZW5vbWJyZW1lbnRDYXJ0ZSA8LSBkZW5vbWJyZW1lbnRDYXJ0ZSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGxhdGluLCB2YWx1ZXNfZnJvbSA9IHN1bSwgdmFsdWVzX2ZpbGwgPSAwKQ0KDQoNCiNvbiB2ZXV0IGFqb3V0ZXIgbGVzIGNvbG9ubmVzIGdlb21ldHJ5LCBidWZmZXIgc2l6ZSBldCBNT1MxMSDDoCBsYSB0YWJsZSBkZW5vbWJyZW1lbnRDYXJ0ZSBldCBwYXMgbGVzIGF1dHJlcw0KDQpkZW5vbWJyZW1lbnRDYXJ0ZSA8LSBsZWZ0X2pvaW4oZGVub21icmVtZW50Q2FydGUsIExVUCwgYnkgPSBjKCJDb2RlX01haWxsZSIgPSAiSUQiKSkNCg0KI29uIGVubMOodmUgbGVzIGNvbG9ubmVzIDogTU9TMSwgTU9TMiwgTU9TMywgTU9TNCwgTU9TNSwgTU9TNiwgTU9TNywgTU9TOCwgTU9TOSwgTU9TMTAsIE1PUzEyLCBNT1MxMywgTU9TMTQNCg0KZGVub21icmVtZW50Q2FydGUgPC0gZGVub21icmVtZW50Q2FydGUgJT4lDQogIHNlbGVjdCgtYyhNT1MxLCBNT1MyLCBNT1MzLCBNT1M0LCBNT1M1LCBNT1M2LCBNT1M3LCBNT1M4LCBNT1M5LCBNT1MxMCwgTU9TMTIsIE1PUzEzLCBNT1MxNCkpDQoNCiMgT24gZ2FyZGUgcXVlIGxlcyBsaWduZXMgb3UgQnVmZmVyU2l6ZSA9PSA1MDANCmRlbm9tYnJlbWVudENhcnRlIDwtIGRlbm9tYnJlbWVudENhcnRlICU+JQ0KICBmaWx0ZXIoQnVmZmVyU2l6ZSA9PSA1MDApDQoNCmRlbm9tYnJlbWVudENhcnRlIDwtIHN0X2FzX3NmKGRlbm9tYnJlbWVudENhcnRlLCBjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gMjE1NCkNCmRlbm9tYnJlbWVudENhcnRlIDwtIHN0X3RyYW5zZm9ybShkZW5vbWJyZW1lbnRDYXJ0ZSwgY3JzID0gNDMyNikNCg0KIyBPbiBnYXJkZSBzZXVlbGVtZW50IGxlcyB2YWxldXJzIG91IGFubmVlID0gMjAxOA0KDQpkZW5vbWJyZW1lbnRDYXJ0ZSA8LSBkZW5vbWJyZW1lbnRDYXJ0ZSAlPiUNCiAgZmlsdGVyKGFubmVlID09IDIwMjMpDQoNCmRlbm9tYnJlbWVudENhcnRlDQpgYGANCkdyw6JjZSDDoCBjZSB0YWJsZWF1LCBub3VzIHBvdXZvbnMgdm9pciBjb21tZW50IGxlcyBkb25uw6llcyBzb250IHN0cnVjdHVyw6llcyBldCByZW1hcnF1ZXIgbm90YW1tZW50IHF1J2lsIGNsYXNzZSBsZSBub21icmUgZCdvaXNlYXV4IHBhciBtYWlsbGUgZXQgcGFyIGFubsOpZS4NCg0KDQpEYW5zIGxhIHN1aXRlIGRlIGNlIHJhcHBvcnQgdm91cyB0cm91dmVyZXogZGVzIGFuYWx5c2VzIGfDqW9ncmFwaGlxdWVzIGV0IGludGVyYWN0aXZlcyBxdWkgdm91cyBwZXJtZXR0cm9udCBkZSB2aXN1YWxpc2VyIGNvbW1lbnQgbGVzIG9pc2VhdXggZGUgbGEgR2lyb25kZSDDqXZvbHVlbnQgZW4gZm9uY3Rpb24gZGUgbCdhcnRpZmljaWFsaXNhdGlvbiBkZXMgc29scy4NClZvdXMgeSB0cm91dmVyZXogZGVzIGFuYWx5c2VzIHN1ciBsZXMgcsOpZ2ltZXMgYWxpbWVudGFpcmVzIGRlcyBvaXNlYXV4LCBsZXMgbml2ZWF1eCBkZSBzcMOpY2lhbGlzYXRpb24gZGVzIG9pc2VhdXggZW4gZm9uY3Rpb24gZGUgbGV1ciBoYWJpdGF0IChNYWlsbGUpLg0KDQpWb2ljaSBsYSBjYXJ0ZSBpbnRlcmFjdGl2ZSBxdWkgbW9udHJlIGxlcyBtYWlsbGVzIGRlIG1lc3VyZSBldCBsZXMgb2lzZWF1eCBsZXMgcGx1cyBvYnNlcnbDqXMgZGFucyBjaGFxdWUgbWFpbGxlLg0KTm91cyB2b3VzIGludml0b25zIMOgIGNsaXF1ZXIgc3VyIGxlcyBjZXJjbGVzIHBvdXIgb2J0ZW5pciB0b3V0ZXMgbGVzIGluZm9ybWF0aW9ucyBzdXIgbGVzIG9pc2VhdXguDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBPbiBnYXJkZXIgcXVlIGxlcyBjb2xvbm5lcyBxdWkgbm91cyBpbnTDqXJlc3NlbnQsIGNhZCBjb2RlX3NpdGUgZXQgTm9tX2xpZXUNCg0KY2l0ZSA8LSByZWFkLmNzdigiZGF0YS9iaXJkcy9CaW9kaXZlckNpdGVfc2l0ZXMuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwPSI7IikNCmNpdGUgPC0gY2l0ZSAlPiUNCiAgc2VsZWN0KGNvZGVfc2l0ZSwgTm9tX2xpZXUpDQoNCiNPbiBqb2luIGxlcyBkZXV4IHRhYmxlcyBjaXRlIGV0IGRlbm9tYnJlbWVudENhcnRlDQoNCmRlbm9tYnJlbWVudENhcnRlIDwtIGxlZnRfam9pbihkZW5vbWJyZW1lbnRDYXJ0ZSwgY2l0ZSwgYnkgPSBjKCJDb2RlX01haWxsZSIgPSAiY29kZV9zaXRlIikpDQoNCiMgQ3LDqWVyIHVuZSBjaGHDrm5lIGRlIGNhcmFjdMOocmVzIHBvdXIgbGVzIHBvcHVwcyBhdmVjIGxlIHRvcCB0cm9pcyBkZXMgb2lzZWF1eCBheWFudCBsYSBwbHVzIGdyYW5kZSB2YWxldXINCmRlbm9tYnJlbWVudENhcnRlJHBvcHVwX3RleHQgPC0gcGFzdGUwKCI8c3Ryb25nPk1haWxsZTo8L3N0cm9uZz4gIiwgZGVub21icmVtZW50Q2FydGUkTm9tX2xpZXUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxzdHJvbmc+QW5uw6llOjwvc3Ryb25nPiAiLCBkZW5vbWJyZW1lbnRDYXJ0ZSRhbm5lZSwgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHN0cm9uZz5Ub3AgMyBkZXMgb2lzZWF1eDo8L3N0cm9uZz4iLCAiPGJyPiIpDQoNCmRhdGFmcmFtZSA8LSBhcy5kYXRhLmZyYW1lKGRlbm9tYnJlbWVudENhcnRlKQ0KDQojIE9uIGVubGV2ZSBsZXMgY29sb25uZXMgQ29kZV9NYWlsbGUsIGFubmVlLCBCdWZmZXJTaXplLCBNT1MxMSwgR2VvbWV0cnksIGdlb21ldHJ5DQpkYXRhZnJhbWUgPC0gZGF0YWZyYW1lICU+JQ0KICBzZWxlY3QoLWMoQ29kZV9NYWlsbGUsIGFubmVlLCBCdWZmZXJTaXplLCBNT1MxMSwgR2VvbWV0cnksIGdlb21ldHJ5LCBOb21fbGlldSkpDQoNCiMgQWpvdXRlciBsZXMgbm9tcyBkZXMgb2lzZWF1eCBldCBsZXVycyB2YWxldXJzIGF1IHBvcHVwX3RleHQgcG91ciBjaGFxdWUgbGlnbmUNCmZvciAoaSBpbiAxOm5yb3coZGF0YWZyYW1lKSkgew0KICB0b3BfYmlyZHMgPC0gc29ydCh1bmxpc3QoZGF0YWZyYW1lW2ksIC1jKG5jb2woZGF0YWZyYW1lKSldKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6M10NCiAgdG9wX2JpcmRfbmFtZXMgPC0gbmFtZXModG9wX2JpcmRzKQ0KICBkZW5vbWJyZW1lbnRDYXJ0ZSRwb3B1cF90ZXh0W2ldIDwtIHBhc3RlMChkZW5vbWJyZW1lbnRDYXJ0ZSRwb3B1cF90ZXh0W2ldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfYmlyZF9uYW1lc1sxXSwgIjogIiwgdG9wX2JpcmRzWzFdLCAiIC0gIiwgdHJhaXRzW3doaWNoKHRyYWl0cyROb20ubGF0aW4gPT0gdG9wX2JpcmRfbmFtZXNbMV0pLCAiTml2ZWF1LmRlLnNww6ljaWFsaXNhdGlvbiJdLCAiPGJyPiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9iaXJkX25hbWVzWzJdLCAiOiAiLCB0b3BfYmlyZHNbMl0sICIgLSAiLCB0cmFpdHNbd2hpY2godHJhaXRzJE5vbS5sYXRpbiA9PSB0b3BfYmlyZF9uYW1lc1syXSksICJOaXZlYXUuZGUuc3DDqWNpYWxpc2F0aW9uIl0sICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX2JpcmRfbmFtZXNbM10sICI6ICIsIHRvcF9iaXJkc1szXSwgIiAtICIsIHRyYWl0c1t3aGljaCh0cmFpdHMkTm9tLmxhdGluID09IHRvcF9iaXJkX25hbWVzWzNdKSwgIk5pdmVhdS5kZS5zcMOpY2lhbGlzYXRpb24iXSkNCn0NCg0KcGFsIDwtIGNvbG9yTnVtZXJpYygidmlyaWRpcyIsIGRvbWFpbiA9IGRlbm9tYnJlbWVudENhcnRlJE1PUzExKQ0KDQoNCmxlYWZsZXQoZGF0YSA9IGRlbm9tYnJlbWVudENhcnRlKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQi5Qb3NpdHJvbiIpICU+JQ0KICBhZGRDaXJjbGVzKHJhZGl1cyA9IDMwMCwgY29sb3IgPSB+cGFsKE1PUzExKSwgZmlsbE9wYWNpdHkgPSAwLjIsIHBvcHVwID0gfnBvcHVwX3RleHQpICU+JQ0KICBhZGRMZWdlbmQoImJvdHRvbXJpZ2h0IiwgcGFsID0gcGFsLCB2YWx1ZXMgPSB+TU9TMTEsIHRpdGxlID0gIk1PUzExIiwgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiKSAlPiUNCiAgYWRkU2NhbGVCYXIocG9zaXRpb24gPSAiYm90dG9tbGVmdCIpDQpgYGANCkdyw6JjZSDDoCBjZXR0ZSBjYXJ0ZSwgbm91cyBvYnRlbm9ucyBsZSBjbGFzc2VtZW50IChUb3AgMykgZGVzIG9pc2VhdXggbGVzIHBsdXMgb2JzZXJ2w6lzIGRhbnMgY2hhcXVlIG1haWxsZSBkZSBtZXN1cmUgcG91ciBsJ2FubsOpZSAyMDIzLg0KTm91cyBhdm9ucyDDqWdhbGVtZW50IGFqb3V0w6kgdW5lIGluZm9ybWF0aW9uIHN1cHBsw6ltZW50YWlyZSBzdXIgY2hhcXVlIG9pc2VhdSBwb3VyIG1ldHRyZSBlbiDDqXZpZGVuY2Ugc29uIG5pdmVhdSBkZSBzcMOpY2lhbGlzYXRpb24gYWZpbiBkZSBtaWV1eCBjb21wcmVuZHJlIGRlIHF1ZWwgdHlwZSBkZSBtaWxpZXUgaWwgYSBiZXNvaW4gcG91ciB2aXZyZSBldCBsYSBnw6lvbG9jYWxpc2F0aW9uIHN1ciBsYSBHaXJvbmRlLg0KDQpOb3VzIHBvdXZvbnMgdm9pciBxdWUgbGUgbml2ZWF1IGRlIHNww6ljaWFsaXNhdGlvbiBsZSBwbHVzIHLDqXBlbmR1IGVzdCBnw6luw6lyYWxpc3RlIGNhciBvbiBlbiByZXRyb3V2ZSBhdSBjZW50cmUtdmlsbGUgZXQgZW4gcMOpcmlwaMOpcmllLg0KRWdhbGVtZW50LCBhdSBjZW50cmUgdmlsbGUsIG9uIHJldHJvdXZlIGRlcyBvaXNlYXV4IGRlIHR5cGUgQsOidGkgZXQgZW4gcMOpcmlwaMOpcmllIGxvaW5zIGR1IGNlbnRyZS12aWxsZSwgb24gcmV0cm91dmUgZGVzIG9pc2VhdXggZGUgdHlwZSBGw7RyZXQgZXQgQWdyaWNvbGUuIFByb2NoZSBkZXMgcG9pbnRzIGQnZWF1LCBvbiB0cm91dmUgZGVzIG9pc2VhdXggZGUgdHlwZSBab25lIGh1bWlkZS4gDQoNCkFpbnNpLCBsZXMgem9uZXMgcXVpIGNvbWJpbmVudCDDoCBsYSBmb2lzIHbDqWfDqXRhdGlvbiBldCBiw6J0aW1lbnRzIHNlcmFpZW50IHBsdXMgc3VzY2VwdGlibGVzIGQnYWNjdWVpbGxpciBkZXMgb2lzZWF1eCBhdmVjIGRlcyBuaXZlYXV4IGRlIHNww6ljaWFsaXNhdGlvbiB2YXJpw6lzLCBjZSBxdWkgZXhwbGlxdWVyYWl0IGwnYXVnbWVudGF0aW9uIGR1IHRhdXggZGUgZGl2ZXJzaXTDqSBvYnNlcnbDqWUgZGFucyBsZXMgem9uZXMgb8O5IHNlIG3DqWxhbmdlbnQgZXNwYWNlcyB2ZXJ0cyBldCB6b25lcyB1cmJhbmlzw6llcy4NCg0KSWwgbm91cyBzZW1ibGFpdCBpbnTDqXJlc3NhbnQgZGUgc2F2b2lyIHF1ZWxzIMOpdGFpZW50IGxlcyByw6lnaW1lcyBhbGltZW50YWlyZXMgbWFqb3JpdGFpcmVzIHByaW5jaXBhdXggZW4gZm9uY3Rpb24gZGUgY2hhcXVlIG1haWxsZS4NCk5vdXMgYXZvbnMgZG9uYyBjcsOpw6kgdW4gZ3JhcGhpcXVlIHF1aSBtb250cmUgbGEgcsOpcGFydGl0aW9uIGRlcyByw6lnaW1lcyBhbGltZW50YWlyZXMgcG91ciBjaGFxdWUgbWFpbGxlLg0KDQpgYGB7cn0NCiMgY2hhbmdlZCB0aGUgdW5tYXRjaGlubmcgbGF0aW4gbmFtZXMgdG8gdGhlIGNvcnJlY3Qgb25lcyB0byBtYXRjaCBhbGltZW50YXRpb24NCmRlbm9tYnJlbWVudCRsYXRpbltkZW5vbWJyZW1lbnQkbGF0aW4gPT0gIkNhcmR1ZWxpcyBjaGxvcmlzIl0gPC0gIkNobG9yaXMgY2hsb3JpcyINCmRlbm9tYnJlbWVudCRsYXRpbltkZW5vbWJyZW1lbnQkbGF0aW4gPT0gIkNhcmR1ZWxpcyBzcGludXMiXSA8LSAiU3BpbnVzIHNwaW51cyINCmRlbm9tYnJlbWVudCRsYXRpbltkZW5vbWJyZW1lbnQkbGF0aW4gPT0gIkNhc21lcm9kaXVzIGFsYnVzIl0gPC0gIkFyZGVhIGFsYmEiDQpkZW5vbWJyZW1lbnQkbGF0aW5bZGVub21icmVtZW50JGxhdGluID09ICJDYXJkdWVsaXMgY2FubmFiaW5hIl0gPC0gIkxpbmFyaWEgY2FubmFiaW5hIg0Kbm9faW5mbyA8LSBjKCJIaW1hbnRvcHVzIGhpbWFudG9wdXMiLCAiVHJpbmdhIG9jaHJvcHVzIiwgDQogICAgICAgICAgICAgIkNhcHJpbXVsZ3VzIGV1cm9wYWV1cyIsICJMYW5pdXMgc2VuYXRvciIsIA0KICAgICAgICAgICAgICJEcnlvY29wdXMgbWFydGl1cyIsICJFbWJlcml6YSBjYWxhbmRyYSIpDQoNCmFsaW1lbnRhdGlvbiA8LSByZWFkLmNzdigiZGF0YS9iaXJkcy90cmFpdHMtc3RhdHV0LUlVQ04tYmlvZGl2ZXJjaXRlLmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkZW5vbWJyZW1lbnQkcmVnaW1lX2FsaW1lbnRhaXJlIDwtIHJlcChOQSwgbnJvdyhkZW5vbWJyZW1lbnQpKQ0KZm9yIChpIGluIDE6bnJvdyhkZW5vbWJyZW1lbnQpKSB7DQogIGlmIChkZW5vbWJyZW1lbnQkbGF0aW5baV0gJWluJSBub19pbmZvKXsNCiAgICBkZW5vbWJyZW1lbnQkcmVnaW1lX2FsaW1lbnRhaXJlW2ldIDwtIE5BDQogIH0NCiAgZWxzZSB7DQogICAgZGVub21icmVtZW50JHJlZ2ltZV9hbGltZW50YWlyZVtpXSA8LSANCiAgICAgIGFsaW1lbnRhdGlvbiRSw6lnaW1lLmFsaW1lbnRhaXJlW3doaWNoKGFsaW1lbnRhdGlvbiROb20ubGF0aW4gPT0gZGVub21icmVtZW50JGxhdGluW2ldKV0NCiAgfQ0KfQ0KZGVub21icmVtZW50JHJlZ2ltZV9hbGltZW50YWlyZSA8LSBhcy5mYWN0b3IoZGVub21icmVtZW50JHJlZ2ltZV9hbGltZW50YWlyZSkNCg0KcGxvdF9kYXRhID0gZnVuY3Rpb24gKGRhdGEsIHRpdGxlKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlcyh4PSIiLCB5PXN1bSwgZmlsbD1yZWdpbWVfYWxpbWVudGFpcmUpKSArDQogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0wLjEpICsNCiAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJTdGF0aW9uOiAiLCBhcy5jaGFyYWN0ZXIodGl0bGUpKSkgKw0KICAgIHRoZW1lX3ZvaWQoKQ0KfQ0KDQpjaXRlIDwtIGNpdGUgJT4lDQogIHNlbGVjdChjb2RlX3NpdGUsIE5vbV9saWV1KQ0KDQojT24gam9pbiBsZXMgZGV1eCB0YWJsZXMgY2l0ZSBldCBkZW5vbWJyZW1lbnRDYXJ0ZQ0KDQpkZW5vbWJyZW1lbnQgPC0gbGVmdF9qb2luKGRlbm9tYnJlbWVudCwgY2l0ZSwgYnkgPSBjKCJDb2RlX01haWxsZSIgPSAiY29kZV9zaXRlIikpDQoNCk4gPC0gbGVuZ3RoKHVuaXF1ZShkZW5vbWJyZW1lbnQkQ29kZV9NYWlsbGUpKQ0KcCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBOKQ0KDQpmb3IgKGkgaW4gMTpOKSB7DQogICAgZGF0YSA8LSBkZW5vbWJyZW1lbnRbd2hpY2goZGVub21icmVtZW50JENvZGVfTWFpbGxlID09IHVuaXF1ZShkZW5vbWJyZW1lbnQkQ29kZV9NYWlsbGUpW2ldKSxdDQogICAgZGF0YSA8LSBkYXRhICU+JSBncm91cF9ieShyZWdpbWVfYWxpbWVudGFpcmUpICU+JSBzdW1tYXJpc2Uoc3VtID0gc3VtKHAsIG5hLnJtID0gVFJVRSkpDQogICAgcFtbaV1dIDwtIHBsb3RfZGF0YShkYXRhLCB1bmlxdWUoZGVub21icmVtZW50JE5vbV9saWV1KVtpXSkNCn0NCg0KY29vcmRpbmF0ZXMgPC0gc3RfYXNfc2YoTFVQLCBjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gMjE1NCkNCmNvb3JkaW5hdGVzIDwtIHN0X3RyYW5zZm9ybShjb29yZGluYXRlcywgY3JzID0gNDMyNikNCmNvb3JkaW5hdGVzIDwtIGNvb3JkaW5hdGVzW2Nvb3JkaW5hdGVzJEJ1ZmZlclNpemUgPT0gNTAwLF0NCg0KIyBSZW1vdmUgdGhlIGNvb3JkaW5hdGVzIHRoYXQgYXJlIG5vdCBpbiBkZW5vbWJyZW1lbnQNCmZvciAoaSBpbiBzZXFfYWxvbmcoY29vcmRpbmF0ZXMkSUQpKXsNCiAgaWYgKCEoY29vcmRpbmF0ZXMkSURbaV0gJWluJSB1bmlxdWUoZGVub21icmVtZW50JENvZGVfTWFpbGxlKSkpew0KICAgIGNvb3JkaW5hdGVzIDwtIGNvb3JkaW5hdGVzWy1pLF0NCiAgfQ0KfQ0KDQpjb29yZGluYXRlcyA8LSBjb29yZGluYXRlc1tvcmRlcihjb29yZGluYXRlcyRJRCwgZGVjcmVhc2luZyA9IFRSVUUpLF0NCg0KcGFsIDwtIGNvbG9yTnVtZXJpYygidmlyaWRpcyIsIGRvbWFpbiA9IGNvb3JkaW5hdGVzJE1PUzExKQ0KbGVhZmxldChkYXRhID0gY29vcmRpbmF0ZXMpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCLlBvc2l0cm9uIikgJT4lDQogIGFkZENpcmNsZXMocmFkaXVzID0gMzAwLCBjb2xvciA9IH5wYWwoTU9TMTEpLCANCiAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuNSwgZ3JvdXAgPSAicG50IikgJT4lDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLCBwYWwgPSBwYWwsIHZhbHVlcyA9IGNvb3JkaW5hdGVzJE1PUzExLCB0aXRsZSA9ICJNT1MxMSIpICU+JQ0KICBhZGRTY2FsZUJhcihwb3NpdGlvbiA9ICJib3R0b21sZWZ0IikgJT4lIA0KICBhZGRQb3B1cEdyYXBocyhwLCB3aWR0aCA9IDIwMCwgaGVpZ2h0ID0gMjAwLCBncm91cCA9ICJwbnQiKQ0KYGBgDQoNClZvaWNpIMOgIHF1b2kgcmVzc2VtYmxlIGxhIGNhcnRlIGludGVyYWN0aXZlIHF1aSBtb250cmUgbGVzIHLDqWdpbWVzIGFsaW1lbnRhaXJlcyBtYWpvcml0YWlyZXMgcHJpbmNpcGF1eCBlbiBmb25jdGlvbiBkZSBjaGFxdWUgbWFpbGxlLiBOb3VzIHBvdXZvbnMgdm9pciBxdSdlbiBnw6luw6lyYWwsIGxlIHLDqWdpbWUgYWxpbWVudGFpcmUgZG9taW5hbnQgZXN0IGxlIHLDqWdpbWUgYWxpbWVudGFpcmUgbWl4dGUsIHF1ZSBjZWxhIHNvaXQgYXUgY2VudHJlIGRlIEJvcmRlYXV4IG91IGVuIHDDqXJpcGjDqXJpZS4gSWwgc2VtYmxlIGRvbmMgcXVlIGxlIHLDqWdpbWUgYWxpbWVudGFpcmUgZGUgbCdvaXNlYXUgbmUgc29pdCBwYXMgdsOpcml0YWJsZW1lbnQgaW1wYWN0w6kgcGFyIGwnYXJ0aWZpY2lhbGlzYXRpb24gZGVzIHNvbHMuDQpFc3NheW9ucyBtYWludGVuYW50IGQnw6l0dWRpZXIgZCdhdXRyZSBjYXJhdMOpcmlzdGlxdWVzIGFmaW4gZCdlc3NheWVyIGRlIHRyb3V2ZXIgZGVzIHJhaXNvbnMgw6AgY2UgcGljIGRlIGRpdmVyc2l0w6kuDQoNCmBgYHtyfQ0KZGVub21icmVtZW50JE5pZGlmaWNhdGlvbiA8LSByZXAoTkEsIG5yb3coZGVub21icmVtZW50KSkNCmZvciAoaSBpbiAxOm5yb3coZGVub21icmVtZW50KSkgew0KICBpZiAoZGVub21icmVtZW50JGxhdGluW2ldICVpbiUgbm9faW5mbyl7DQogICAgZGVub21icmVtZW50JE5pZGlmaWNhdGlvbltpXSA8LSBOQQ0KICB9DQogIGVsc2Ugew0KICAgIGRlbm9tYnJlbWVudCROaWRpZmljYXRpb25baV0gPC0gDQogICAgICBhbGltZW50YXRpb24kTmlkaWZpY2F0aW9uW3doaWNoKGFsaW1lbnRhdGlvbiROb20ubGF0aW4gPT0gZGVub21icmVtZW50JGxhdGluW2ldKV0NCiAgfQ0KfQ0KZGVub21icmVtZW50JE5pZGlmaWNhdGlvbiA8LSBhcy5mYWN0b3IoZGVub21icmVtZW50JE5pZGlmaWNhdGlvbikNCg0KcGxvdF9kYXRhID0gZnVuY3Rpb24gKGRhdGEsIHRpdGxlKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlcyh4PSIiLCB5PXN1bSwgZmlsbD1OaWRpZmljYXRpb24pKSArDQogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0wLjEpICsNCiAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJTdGF0aW9uOiAiLCBhcy5jaGFyYWN0ZXIodGl0bGUpKSkgKw0KICAgIHRoZW1lX3ZvaWQoKQ0KfQ0KDQojT24gam9pbiBsZXMgZGV1eCB0YWJsZXMgY2l0ZSBldCBkZW5vbWJyZW1lbnRDYXJ0ZQ0KDQpOIDwtIGxlbmd0aCh1bmlxdWUoZGVub21icmVtZW50JENvZGVfTWFpbGxlKSkNCnAgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gTikNCg0KZm9yIChpIGluIDE6Tikgew0KICAgIGRhdGEgPC0gZGVub21icmVtZW50W3doaWNoKGRlbm9tYnJlbWVudCRDb2RlX01haWxsZSA9PSB1bmlxdWUoZGVub21icmVtZW50JENvZGVfTWFpbGxlKVtpXSksXQ0KICAgIGRhdGEgPC0gZGF0YSAlPiUgZ3JvdXBfYnkoTmlkaWZpY2F0aW9uKSAlPiUgc3VtbWFyaXNlKHN1bSA9IHN1bShwLCBuYS5ybSA9IFRSVUUpKQ0KICAgIHBbW2ldXSA8LSBwbG90X2RhdGEoZGF0YSwgdW5pcXVlKGRlbm9tYnJlbWVudCROb21fbGlldSlbaV0pDQp9DQoNCmNvb3JkaW5hdGVzIDwtIHN0X2FzX3NmKExVUCwgY29vcmRzID0gYygiWCIsICJZIiksIGNycyA9IDIxNTQpDQpjb29yZGluYXRlcyA8LSBzdF90cmFuc2Zvcm0oY29vcmRpbmF0ZXMsIGNycyA9IDQzMjYpDQpjb29yZGluYXRlcyA8LSBjb29yZGluYXRlc1tjb29yZGluYXRlcyRCdWZmZXJTaXplID09IDUwMCxdDQoNCiMgUmVtb3ZlIHRoZSBjb29yZGluYXRlcyB0aGF0IGFyZSBub3QgaW4gZGVub21icmVtZW50DQpmb3IgKGkgaW4gc2VxX2Fsb25nKGNvb3JkaW5hdGVzJElEKSl7DQogIGlmICghKGNvb3JkaW5hdGVzJElEW2ldICVpbiUgdW5pcXVlKGRlbm9tYnJlbWVudCRDb2RlX01haWxsZSkpKXsNCiAgICBjb29yZGluYXRlcyA8LSBjb29yZGluYXRlc1staSxdDQogIH0NCn0NCg0KY29vcmRpbmF0ZXMgPC0gY29vcmRpbmF0ZXNbb3JkZXIoY29vcmRpbmF0ZXMkSUQsIGRlY3JlYXNpbmcgPSBUUlVFKSxdDQoNCnBhbCA8LSBjb2xvck51bWVyaWMoInZpcmlkaXMiLCBkb21haW4gPSBjb29yZGluYXRlcyRNT1MxMSkNCmxlYWZsZXQoZGF0YSA9IGNvb3JkaW5hdGVzKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQi5Qb3NpdHJvbiIpICU+JQ0KICBhZGRDaXJjbGVzKHJhZGl1cyA9IDMwMCwgY29sb3IgPSB+cGFsKE1PUzExKSwgDQogICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsIGdyb3VwID0gInBudDIiKSAlPiUNCiAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbCA9IHBhbCwgdmFsdWVzID0gY29vcmRpbmF0ZXMkTU9TMTEsIHRpdGxlID0gIk1PUzExIikgJT4lDQogIGFkZFNjYWxlQmFyKHBvc2l0aW9uID0gImJvdHRvbWxlZnQiKSAlPiUgDQogIGFkZFBvcHVwR3JhcGhzKHAsIHdpZHRoID0gMjAwLCBoZWlnaHQgPSAyMDAsIGdyb3VwID0gInBudDIiKQ0KYGBgDQoNCkNldHRlIGNhcnRlIGlsbHVzdHJlIGxhIHLDqXBhcnRpdGlvbiBkZXMgdHlwZXMgZGUgbmlkaWZpY2F0aW9uIGVuIGZvbmN0aW9uIGRlcyB6b25lcyBnw6lvZ3JhcGhpcXVlcy4gSWwgZXN0IG9ic2VydsOpIHF1ZSBkYW5zIGxlcyB6b25lcyBjZW50cmFsZXMsIGxhIG1ham9yaXTDqSBkZXMgbmlkcyBzb250IHNpdHXDqXMgZGFucyBkZXMgY2F2aXTDqXMsIGNlIHF1aSBwZXV0IMOqdHJlIGF0dHJpYnXDqSDDoCBsYSBkZW5zaXTDqSDDqWxldsOpZSBkZSBiw6J0aW1lbnRzIGV0IMOgIGxhIHJhcmV0w6kgZGUgbGEgdsOpZ8OpdGF0aW9uIGVuIHZpbGxlLiBMZSBtb2RlIGRlIG5pZGlmaWNhdGlvbiBhdSBzb2wgZXN0IGxlIG1vaW5zIGNvdXJhbnQgZGFucyBjZXMgem9uZXMsIHByb2JhYmxlbWVudCBlbiByYWlzb24gZGUgbGEgZmFpYmxlIHByb2JhYmlsaXTDqSBkZSBzdXJ2aWUgZGVzIG9pc2VhdXggZGFucyB1biBlbnZpcm9ubmVtZW50IHVyYmFpbiBhdmVjIHVuIG5pZCBhdSBzb2wuICANCg0KRW4gcMOpcmlwaMOpcmllLCBsZXMgbW9kZXMgZGUgbmlkaWZpY2F0aW9uIHNlbWJsZW50IHBsdXMgZGl2ZXJzaWZpw6lzLCBhdmVjIHVuZSBwcsOpZG9taW5hbmNlIGRlIGxhIG5pZGlmaWNhdGlvbiBlbiBidWlzc29uLiBMYSBuaWRpZmljYXRpb24gZGFucyBsZXMgYXJicmVzIGVzdCDDqWdhbGVtZW50IHRyw6hzIHLDqXBhbmR1ZSwgZXQgb24gcmV0cm91dmUgYXVzc2kgbGEgbmlkaWZpY2F0aW9uIGVuIGNhdml0w6kuIExlIGZhaXQgZGUgbmUgcGFzIMOqdHJlIHNpdHXDqSBkaXJlY3RlbWVudCBhdSBjZW50cmUtdmlsbGUgc2VtYmxlIG9mZnJpciBwbHVzIGRlIHBvc3NpYmlsaXTDqXMgZGUgbmlkaWZpY2F0aW9uLCBjZSBxdWkgcG91cnJhaXQgZXhwbGlxdWVyIHVuZSBwbHVzIGdyYW5kZSBkaXZlcnNpdMOpIGQnZXNww6hjZXMgc2kgb24gcyfDqWxvaWduZSBsw6lnw6hyZW1lbnQgZHUgY2VudHJlIHZpbGxlLCBncsOiY2Ugw6AgdW5lIGNvbWJpbmFpc29uIGRlIGLDonRpbWVudHMgZXQgZGUgdsOpZ8OpdGF0aW9uIG9mZnJhbnQgcGx1cyBkZSBjaG9peCBwb3VyIGxhIG5pZGlmaWNhdGlvbi4NCg0KIyBQb3VyIGFsbGVyIHBsdXMgbG9pbg0KDQpDZXR0ZSBzZWN0aW9uIGVzdCBkw6lkacOpZSDDoCBkZXMgYW5hbHlzZXMgcGx1cyBwb3Vzc8OpZXMgcXVpIHBvdXJyYWllbnQgw6p0cmUgcsOpYWxpc8OpZXMgcG91ciBtaWV1eCBjb21wcmVuZHJlIGxlcyBkb25uw6llcyBldCBsZXMgcmVsYXRpb25zIGVudHJlIGxlcyBkaWZmw6lyZW50ZXMgdmFyaWFibGVzLg0KVm91cyB0cm91dmVyZXogdW4gUGFyYWxsZWwgQ29vcmRpbmF0ZXMgUGxvdCBhaW5zaSBxdSd1biBzdW4gYnVyc3QgcGxvdCBxdWkgaWxsdXN0cmVudCBsZXMgY2FyYWN0w6lyaXN0aXF1ZXMgZGVzIGRpZmbDqXJlbnRlcyBlc3DDqGNlcyBkJ29pc2VhdXguDQoNClZvaWNpLCBjaS1kZXNzb3VzIGxlIFBhcmFsbGVsIENvb3JkaW5hdGVzIFBsb3QgaW50w6lyYWN0aWYuDQpWb3VzIHBvdXZleiBjbGlxdWVyIHN1ciBsZXMgZGlmZsOpcmVudGVzIGVzcMOoY2VzIHBvdXIgb2J0ZW5pciBwbHVzIGQnaW5mb3JtYXRpb25zIHN1ciBjaGFjdW5lIGQnZWxsZXMuDQoNCmBgYHtyfQ0KIyAtLS0gTG9hZCBkYXRhIC0tLQ0KDQpiaXJkc19pbmZvIDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL3RyYWl0cy1zdGF0dXQtSVVDTi1iaW9kaXZlcmNpdGUuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmJpcmRzX2luZm8gPC0gYmlyZHNfaW5mb1ssIGMoDQogICJOb20ubGF0aW4iLA0KICAiTml2ZWF1LmRlLnNww6ljaWFsaXNhdGlvbiIsDQogICJSw6lnaW1lLmFsaW1lbnRhaXJlIiwNCiAgIlRlY2huaXF1ZS5kLmFsaW1lbnRhdGlvbiIsDQogICJOaWRpZmljYXRpb24iLA0KICAiUMOpcmlvZGUuZGUubWlncmF0aW9uIg0KKV0NCg0KIyAtLS0gUGFyY29vcmRzIGRhdGEgcHJlcGFyYXRpb24gLS0tDQoNCnVuaXF1ZV9sZXZlbCA8LSB1bmlxdWUoYmlyZHNfaW5mb1ssICJOaXZlYXUuZGUuc3DDqWNpYWxpc2F0aW9uIl0pDQp1bmlxdWVfcmVnaW1lIDwtIHVuaXF1ZShiaXJkc19pbmZvWywgIlLDqWdpbWUuYWxpbWVudGFpcmUiXSkNCnVuaXF1ZV90ZWNobmlxdWUgPC0gdW5pcXVlKGJpcmRzX2luZm9bLCAiVGVjaG5pcXVlLmQuYWxpbWVudGF0aW9uIl0pDQp1bmlxdWVfbmlkaSA8LSB1bmlxdWUoYmlyZHNfaW5mb1ssICJOaWRpZmljYXRpb24iXSkNCnVuaXF1ZV9taWdyYXRpb24gPC0gdW5pcXVlKGJpcmRzX2luZm9bLCAiUMOpcmlvZGUuZGUubWlncmF0aW9uIl0pDQoNCnBsb3RfbHkoZGF0YS5mcmFtZSgpLCB0eXBlID0gInBhcmNvb3JkcyIsIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsdWUiKSwgaGVpZ2h0ID0gOTUwLA0KICBkaW1lbnNpb25zID0gbGlzdCgNCiAgICBsaXN0KA0KICAgICAgbGFiZWwgPSAiRXNww6hjZXMiLA0KICAgICAgcmFuZ2UgPSBjKDAsIGxlbmd0aChiaXJkc19pbmZvWywgIk5vbS5sYXRpbiJdKSArIDEpLA0KICAgICAgdGlja3ZhbHMgPSAxOmxlbmd0aChiaXJkc19pbmZvWywgIk5vbS5sYXRpbiJdKSwNCiAgICAgIHRpY2t0ZXh0ID0gYmlyZHNfaW5mb1ssICJOb20ubGF0aW4iXSwNCiAgICAgIHZhbHVlcyA9IDE6bGVuZ3RoKGJpcmRzX2luZm9bLCAiTm9tLmxhdGluIl0pDQogICAgKSwNCiAgICBsaXN0KA0KICAgICAgbGFiZWwgPSAiVGVjaG5pcXVlIGQnYWxpbWVudGF0aW9uIiwNCiAgICAgIHJhbmdlID0gYygwLCBsZW5ndGgodW5pcXVlX3RlY2huaXF1ZSkgKyAxKSwNCiAgICAgIHRpY2t2YWxzID0gYXMubnVtZXJpYyhmYWN0b3IodW5pcXVlX3RlY2huaXF1ZSkpLA0KICAgICAgdGlja3RleHQgPSB1bmlxdWVfdGVjaG5pcXVlLA0KICAgICAgdmFsdWVzID0gYXMubnVtZXJpYyhmYWN0b3IoYmlyZHNfaW5mb1ssICJUZWNobmlxdWUuZC5hbGltZW50YXRpb24iXSkpDQogICAgKSwNCiAgICBsaXN0KA0KICAgICAgbGFiZWwgPSAiTmlkaWZpY2F0aW9uIiwNCiAgICAgIHJhbmdlID0gYygwLCBsZW5ndGgodW5pcXVlX25pZGkpICsgMSksDQogICAgICB0aWNrdmFscyA9IGFzLm51bWVyaWMoZmFjdG9yKHVuaXF1ZV9uaWRpKSksDQogICAgICB0aWNrdGV4dCA9IHVuaXF1ZV9uaWRpLA0KICAgICAgdmFsdWVzID0gYXMubnVtZXJpYyhmYWN0b3IoYmlyZHNfaW5mb1ssICJOaWRpZmljYXRpb24iXSkpDQogICAgKSwNCiAgICBsaXN0KA0KICAgICAgbGFiZWwgPSAiTml2ZWF1IGRlIHNww6ljaWFsaXNhdGlvbiIsDQogICAgICByYW5nZSA9IGMoMCwgbGVuZ3RoKHVuaXF1ZV9sZXZlbCkgKyAxKSwNCiAgICAgIHRpY2t2YWxzID0gYXMubnVtZXJpYyhmYWN0b3IodW5pcXVlX2xldmVsKSksDQogICAgICB0aWNrdGV4dCA9IHVuaXF1ZV9sZXZlbCwNCiAgICAgIHZhbHVlcyA9IGFzLm51bWVyaWMoZmFjdG9yKGJpcmRzX2luZm9bLCAiTml2ZWF1LmRlLnNww6ljaWFsaXNhdGlvbiJdKSkNCiAgICApLA0KICAgIGxpc3QoDQogICAgICBsYWJlbCA9ICJQw6lyaW9kZSBkZSBtaWdyYXRpb24iLA0KICAgICAgcmFuZ2UgPSBjKDAsIGxlbmd0aCh1bmlxdWVfbWlncmF0aW9uKSArIDEpLA0KICAgICAgdGlja3ZhbHMgPSBhcy5udW1lcmljKGZhY3Rvcih1bmlxdWVfbWlncmF0aW9uKSksDQogICAgICB0aWNrdGV4dCA9IHVuaXF1ZV9taWdyYXRpb24sDQogICAgICB2YWx1ZXMgPSBhcy5udW1lcmljKGZhY3RvcihiaXJkc19pbmZvWywgIlDDqXJpb2RlLmRlLm1pZ3JhdGlvbiJdKSkNCiAgICApLA0KICAgIGxpc3QoDQogICAgICBsYWJlbCA9ICJSw6lnaW1lIGFsaW1lbnRhaXJlIiwNCiAgICAgIHJhbmdlID0gYygwLCBsZW5ndGgodW5pcXVlX3JlZ2ltZSkgKyAxKSwNCiAgICAgIHRpY2t2YWxzID0gYXMubnVtZXJpYyhmYWN0b3IodW5pcXVlX3JlZ2ltZSkpLA0KICAgICAgdGlja3RleHQgPSB1bmlxdWVfcmVnaW1lLA0KICAgICAgdmFsdWVzID0gYXMubnVtZXJpYyhmYWN0b3IoYmlyZHNfaW5mb1ssICJSw6lnaW1lLmFsaW1lbnRhaXJlIl0pKQ0KICAgICkNCiAgKQ0KKSAlPiUgbGF5b3V0KA0KICB0aXRsZSA9ICJDYXJhY3TDqXJpc3RpcXVlcyBkZXMgZGlmZsOpcmVudGVzIGVzcMOoY2VzIGQnb2lzZWF1eCIsDQogIG1hcmdpbiA9IGxpc3QobCA9IDE0MCwgciA9IDU1LCBiID0gMCkNCikNCmBgYA0KDQojIyMgRXhlbXBsZSBkJ3V0aWxpc2F0aW9uIDoNCg0KLSBOb3VzIHZvdWxvbnMgc2F2b2lyIGxlIHLDqWdpbWUgYWxpbWVudGFpcmUgZGVzIG9pc2VhdXggcXVpIG1pZ3JlbnQuIA0KUG91ciBjZWxhLCBpbCBzdWZmaXQgZGUgY2xpcXVlciBhdSBuaXZlYXUgZGUgIm1pZ3JhdGV1ciB0YXJkaWYiIGV0IGRlIGNsaXF1ZXIgc3VyICJWw6lnw6l0YXJpZW4iLCAiTWl4dGUiIG91ICJjYXJuaXZvcmUiIHBvdXIgb2J0ZW5pciBsZXMgZXNww6hjZXMgZCdvaXNlYXV4IHF1aSBjb3JyZXNwb25kZW50IMOgIGNlcyBjcml0w6hyZXMuDQpDZWNpIGVzdCB0csOocyB1dGlsZSBsb3JzcXUnb24gdmV1dCBjb21wYXJlciBsZSBub21icmUgZCdvaXNlYXUgc2Vsb24gbGVzIGNhcmFjdMOpcmlzdGlxdWVzIGNob2lzaWVzIG91IG3Dqm1lIHNpbXBsZW1lbnQgcmVjaGVyY2hlciBsJ2VzcMOoY2UgcXVpIGNvcnJlc3BvbmQgw6AgY2VydGFpbnMgY3JpdMOocmVzIHF1ZSBsJ29uIHZldXQgw6l0dWRpZXIuDQpQb3VyIGZpbmlyIGwnZXhlbXBsZSwgc2kgb24gY2xpcXVlIGF1IG5pdmVhdSBkZSAiTWlncmF0ZXVyIHRhcmRpZiIsIG9uIHJlbWFycXVlIHF1J2F1Y3VuIG9pc2VhdSBuJ2VzdCB2w6lnw6l0YXJpZW4uDQpEZSBwbHVzLCBvbiBwZXV0IGFmZmluZXIgbm90cmUgc8OpbGVjdGlvbiBwb3VyIHZvaXIgcXVlbGxlcyBlc3DDqGNlcyBkJ29pc2VhdXggc29udCBkZXMgbWlncmF0ZXVycyB0YXJkaWZzIGV0IGZvbnQgbGV1ciBuaWQgZGFucyBsZXMgYnVpc3NvbnMuDQpPbiBjbGlxdWUgYWluc2kgc3VyIGJ1aXNzb24gZXQgb24gdm9pdCBxdWUgbCdlc3DDqGNlICJMYW5pdXMgY29sbHVyaW8iIGNvcnJlc3BvbmQgw6AgY2VzIGNyaXTDqHJlcy4NCkMnZXN0IHVuZSBlc3DDqGNlIGNhcm5pdm9yZSBxdWkgbWlncmUgdGFyZGl2ZW1lbnQgZXQgZmFpdCBzb24gbmlkIGRhbnMgbGVzIGJ1aXNzb25zIHF1aSBhIHVuIG5pdmVhdSBkZSBzcMOpY2lhbGlzYXRpb24gYWdyaWNvbGUgZXQgcXVpIHMnYWxpbWVudGUgZW4gdm9sLg0KDQo8Y2VudGVyPg0KICAhW1ZvaWNpIHVuZSBpbWFnZSBkZSBsYSBwaWUtZ3Jpw6hjaGUgw6ljb3JjaGV1ciAoTGFuaXVzIGNvbGx1cmlvKSwgd2lraXBlZGlhXShodHRwczovL3VwbG9hZC53aWtpbWVkaWEub3JnL3dpa2lwZWRpYS9jb21tb25zL2IvYjAvTGFuaXVzX2NvbGx1cmlvXzUuanBnKQ0KPC9jZW50ZXI+DQoNCmBgYHtyfQ0KIyAtLS0gTG9hZCBkYXRhIC0tLQ0KDQpMVVAgPC0gcmVhZC5jc3YoImRhdGEvYmlyZHMvTGFuZFVzZVBlcl9CTV8yMDIzX2NhcnRvSVNlYS5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KTFVQIDwtIExVUFssIGMoYygiSUQiLCAiQnVmZmVyU2l6ZSIpLCBwYXN0ZTAoIk1PUyIsIDE6MTQpKV0NCg0KYmlyZHNfb2JzIDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL09pc2VhdXhfdXBfdG9fMjAyMy5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiKQ0KYmlyZHNfb2JzIDwtIGJpcmRzX29ic1ssIGMoYygiQ29kZV9NYWlsbGUiLCAiRGF0ZSIsICJOb21fVGF4b25fQ2l0ZSIsICJEZW5vbWJyZW1lbnRfbWluIikpXQ0KYmlyZHNfb2JzIDwtIGJpcmRzX29icyAlPiUNCiAgcmVuYW1lKFllYXIgPSBEYXRlLCBJRCA9IENvZGVfTWFpbGxlLCBMYXRpbiA9IE5vbV9UYXhvbl9DaXRlKQ0KDQpiaXJkc19vYnNbLCAiWWVhciJdIDwtIHN1YnN0cihiaXJkc19vYnNbLCAiWWVhciJdLCBzdGFydCA9IDEsIHN0b3AgPSA0KQ0KYmlyZHNfb2JzWywgIkxhdGluIl0gPC0gc2FwcGx5KHN0cnNwbGl0KGJpcmRzX29ic1ssICJMYXRpbiJdLCBzcGxpdCA9ICIgfCAiLCBmaXhlZCA9IFRSVUUpLCBmdW5jdGlvbih4KSB4WzFdKQ0KDQpiaXJkc19vYnMgPC0gYmlyZHNfb2JzICU+JQ0KICBncm91cF9ieShJRCwgWWVhciwgTGF0aW4pICU+JQ0KICBzdW1tYXJpc2UoU3VtID0gc3VtKERlbm9tYnJlbWVudF9taW4pLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQpMVVBfYmlyZHNfb2JzIDwtIG1lcmdlKExVUCwgYmlyZHNfb2JzLCBieSA9ICJJRCIpDQoNCiMgLS0tIFN1bmJ1cnN0IGRhdGEgcHJlcGFyYXRpb24gLS0tDQoNCnVuaXF1ZV95ZWFyIDwtIHVuaXF1ZShMVVBfYmlyZHNfb2JzWywgIlllYXIiXSkNCnVuaXF1ZV95ZWFyIDwtIHVuaXF1ZV95ZWFyW29yZGVyKHVuaXF1ZV95ZWFyKV0NCg0KaWRzIDwtIGMoIkFubsOpZSIsIHVuaXF1ZV95ZWFyKQ0KZm9yICh5ZWFyIGluIHVuaXF1ZV95ZWFyKSB7DQogIGlkcyA8LSBjKGlkcywgcGFzdGUwKHllYXIsIHBhc3RlMCgiIC0gTU9TIiwgMToxNCkpKQ0KfQ0KDQpmb3IgKHllYXIgaW4gdW5pcXVlX3llYXIpIHsNCiAgZm9yIChtb3MgaW4gcGFzdGUwKCJNT1MiLCAxOjE0KSkgew0KICAgIGZvciAoc3BlY2llIGluIGJpcmRzX2luZm9bLCAiTm9tLmxhdGluIl0pIHsNCiAgICAgIGlkcyA8LSBjKGlkcywgcGFzdGUwKHllYXIsIHBhc3RlMChwYXN0ZTAoIiAtICIsIG1vcyksIHBhc3RlMCgiIC0gIiwgc3BlY2llKSkpKQ0KICAgIH0NCiAgfQ0KfQ0KDQpsYWJlbHMgPC0gYygiQW5uw6llIiwgdW5pcXVlX3llYXIsIHJlcChwYXN0ZTAoIk1PUyIsIDE6MTQpLCB0aW1lcyA9IGxlbmd0aCh1bmlxdWVfeWVhcikpKQ0KbGFiZWxzIDwtIGMobGFiZWxzLCByZXAoYmlyZHNfaW5mb1ssICJOb20ubGF0aW4iXSwgdGltZXMgPSAxNCAqIGxlbmd0aCh1bmlxdWVfeWVhcikpKQ0KDQpwYXJlbnRzIDwtIGMoIiIsIHJlcCgiQW5uw6llIiwgdGltZXMgPSBsZW5ndGgodW5pcXVlX3llYXIpKSwgcmVwKHVuaXF1ZV95ZWFyLCBlYWNoID0gMTQpKQ0KZm9yICh5ZWFyIGluIHVuaXF1ZV95ZWFyKSB7DQogIHBhcmVudHMgPC0gYyhwYXJlbnRzLCBwYXN0ZTAoeWVhciwgcmVwKHBhc3RlMCgiIC0gTU9TIiwgMToxNCksIGVhY2ggPSBsZW5ndGgoYmlyZHNfaW5mb1ssICJOb20ubGF0aW4iXSkpKSkNCn0NCg0KdmFsdWVzIDwtIGMoYyhsZW5ndGgodW5pcXVlX3llYXIpICogMTQwMDAsIHJlcCgxNDAwMCwgdGltZXMgPSBsZW5ndGgodW5pcXVlX3llYXIpKSksIHJlcCgxMDAwLCB0aW1lcyA9IGxlbmd0aCh1bmlxdWVfeWVhcikgKiAxNCkpDQpmb3IgKHllYXIgaW4gdW5pcXVlX3llYXIpIHsNCiAgZm9yIChtb3MgaW4gcGFzdGUwKCJNT1MiLCAxOjE0KSkgew0KICAgIGZvciAoc3BlY2llIGluIGJpcmRzX2luZm9bLCAiTm9tLmxhdGluIl0pIHsNCiAgICAgIHRtcCA8LSBMVVBfYmlyZHNfb2JzW0xVUF9iaXJkc19vYnNbIlllYXIiXSA9PSB5ZWFyICYgTFVQX2JpcmRzX29ic1siTGF0aW4iXSA9PSBzcGVjaWUgJiBMVVBfYmlyZHNfb2JzWyJCdWZmZXJTaXplIl0gPT0gNTAwLCBdDQogICAgICB2YWx1ZXMgPC0gYyh2YWx1ZXMsIHN1bSh0bXBbbW9zXSAqIHRtcFsiU3VtIl0pKQ0KICAgIH0NCiAgICB0YWlsX3ZhbHVlcyA8LSB2YWx1ZXNbKDEgKyBsZW5ndGgodmFsdWVzKSAtIGxlbmd0aChiaXJkc19pbmZvWywgIk5vbS5sYXRpbiJdKSk6bGVuZ3RoKHZhbHVlcyldDQogICAgdmFsdWVzWygxICsgbGVuZ3RoKHZhbHVlcykgLSBsZW5ndGgoYmlyZHNfaW5mb1ssICJOb20ubGF0aW4iXSkpOmxlbmd0aCh2YWx1ZXMpXSA8LSB0YWlsX3ZhbHVlcyAvIHN1bSh0YWlsX3ZhbHVlcykgKiAxMDAwDQogIH0NCn0NCg0KaXNfbmFuX3ZhbHVlcyA8LSAhaXMubmEodmFsdWVzKSAmIHZhbHVlcyAhPSAwDQppZHMgPC0gaWRzW2lzX25hbl92YWx1ZXNdDQpsYWJlbHMgPC0gbGFiZWxzW2lzX25hbl92YWx1ZXNdDQpwYXJlbnRzIDwtIHBhcmVudHNbaXNfbmFuX3ZhbHVlc10NCnZhbHVlcyA8LSB2YWx1ZXNbaXNfbmFuX3ZhbHVlc10NCg0KIyAtLS0gU3VuYnVyc3QgZGlzcGxheSAtLS0NCg0KcGxvdF9seSgNCiAgaWRzID0gaWRzLA0KICBsYWJlbHMgPSBsYWJlbHMsDQogIHBhcmVudHMgPSBwYXJlbnRzLA0KICB2YWx1ZXMgPSB2YWx1ZXMsDQogIHR5cGUgPSAic3VuYnVyc3QiLA0KICBicmFuY2h2YWx1ZXMgPSAidG90YWwiLA0KICBtYXhkZXB0aCA9IDIsDQogIGluc2lkZXRleHRvcmllbnRhdGlvbiA9ICJyYWRpYWwiLA0KICBob3ZlcmluZm8gPSAibGFiZWwrcGVyY2VudCBlbnRyeSIsDQogIGhlaWdodCA9IDgwMA0KKSAlPiUgbGF5b3V0KA0KICB0aXRsZSA9IGxpc3QoDQogICAgdGV4dCA9ICJQcm9wb3J0aW9uIGRlcyBlc3DDqGNlcyBkJ29pc2VhdXggbGVzIHBsdXMgY291cmFtbWVudCBvYnNlcnbDqWVzPGJyPmVuIGZvbmN0aW9uIGRlIGwnYW5uw6llIGV0IGR1IE1PUyIsDQogICAgeSA9IDEuMQ0KICApLA0KICBtYXJnaW4gPSBsaXN0KHQgPSAxMDApDQopDQpgYGANCg0KIyBDb25jbHVzaW9uDQoNCg0KQXByw6hzIGF2b2lyIG1lbsOpIHVuZSDDqXR1ZGUgYXBwcm9mb25kaWUgc3VyIGxhIGRpdmVyc2l0w6kgZGVzIG9pc2VhdXggw6AgQm9yZGVhdXggZXQgc2EgcMOpcmlwaMOpcmllLCBub3VzIGF2b25zIHB1IGZhaXJlIHBsdXNpZXVycyBkw6ljb3V2ZXJ0ZXMgaW50w6lyZXNzYW50ZXMgZW4gdXRpbGlzYW50IGRpZmbDqXJlbnRlcyBtZXN1cmVzIGRlIGRpdmVyc2l0w6kgdGVsbGVzIHF1ZSBsYSByaWNoZXNzZSBzcMOpY2lmaXF1ZSwgbCdpbmRpY2UgZGUgU2hhbm5vbiBldCBsJ2luZGljZSBkZSBTaW1wc29uLg0KDQpUb3V0IGQnYWJvcmQsIG5vdXMgYXZvbnMgZMOpY291dmVydCBxdWUgbGEgZGl2ZXJzaXTDqSBkZXMgZXNww6hjZXMgZCdvaXNlYXV4IGVzdCDDqXRyb2l0ZW1lbnQgbGnDqWUgYXUgbml2ZWF1IGQnYXJ0aWZpY2lhbGlzYXRpb24gZGVzIHNvbHMsIG1lc3Vyw6kgcGFyIGxhIHZhcmlhYmxlIE1PUzExLiBFbiBlZmZldCwgbm91cyBhdm9ucyBjb25zdGF0w6kgcXVlIGxhIGRpdmVyc2l0w6kgZXN0IHBsdXMgw6lsZXbDqWUgZGFucyBsZXMgem9uZXMgb8O5IHNlIG3DqWxhbmdlbnQgZXNwYWNlcyB2ZXJ0cyBldCB6b25lcyB1cmJhbmlzw6llcy4gQ2V0dGUgb2JzZXJ2YXRpb24gc3VnZ8OocmUgcXVlIGxlcyB6b25lcyB1cmJhaW5lcyBxdWkgcHLDqXNlcnZlbnQgZGVzIGVzcGFjZXMgdmVydHMgZXQgZGVzIGhhYml0YXRzIG5hdHVyZWxzIHNvbnQgcGx1cyBzdXNjZXB0aWJsZXMgZCdhY2N1ZWlsbGlyIHVuZSBncmFuZGUgZGl2ZXJzaXTDqSBkJ2VzcMOoY2VzIGQnb2lzZWF1eC4NCg0KRW5zdWl0ZSwgbm91cyBhdm9ucyDDqXR1ZGnDqSBsYSByZWxhdGlvbiBlbnRyZSBsYSBkaXZlcnNpdMOpIGRlcyBlc3DDqGNlcyBkJ29pc2VhdXggZXQgbGEgZGlzdGFuY2UgYXUgY2VudHJlLXZpbGxlLiBOb3VzIGF2b25zIGNvbnN0YXTDqSBxdWUgbGEgZGl2ZXJzaXTDqSBlc3QgcGx1cyBncmFuZGUgbG9yc3F1ZSBsYSBtYWlsbGUgc2UgdHJvdXZlIMOgIGVudmlyb24gOSBrbSBkdSBjZW50cmUtdmlsbGUuIENldHRlIHRlbmRhbmNlIHMnZXhwbGlxdWUgZW4gcGFydGllIHBhciBsZSBmYWl0IHF1ZSBsZXMgem9uZXMgc2l0dcOpZXMgw6AgY2V0dGUgZGlzdGFuY2UgZHUgY2VudHJlLXZpbGxlIHNvbnQgc291dmVudCBkZXMgem9uZXMgZGUgdHJhbnNpdGlvbiBlbnRyZSBsZXMgZXNwYWNlcyB1cmJhaW5zIGV0IGxlcyBlc3BhY2VzIHJ1cmF1eCwgb2ZmcmFudCBhaW5zaSB1bmUgdmFyacOpdMOpIGQnaGFiaXRhdHMgcG91ciBsZXMgb2lzZWF1eC4NCg0KUGFyIGxhIHN1aXRlLCBub3VzIGF2b25zIGVzc2F5w6kgZGUgdHJvdXZlciBkZXMgbGllbnMgZW50cmUgbGEgZGl2ZXJzaXTDqSBkZXMgZXNww6hjZXMgZCdvaXNlYXV4IGV0IGQnYXV0cmVzIGNhcmFjdMOpcmlzdGlxdWVzIHRlbGxlcyBxdWUgbGVzIHLDqWdpbWVzIGFsaW1lbnRhaXJlcywgbGVzIG5pdmVhdXggZGUgc3DDqWNpYWxpc2F0aW9uIGV0IGxlcyBtb2RlcyBkZSBuaWRpZmljYXRpb24uDQoNClBvdXIgbGVzIG5pdmVhdXggZGUgc3DDqWNpYWxpc2F0aW9uLCBub3VzIGF2b25zIG9ic2VydsOpIHF1ZSBsZXMgb2lzZWF1eCBkZSB0eXBlIELDonRpIHNvbnQgcGx1cyByw6lwYW5kdXMgZGFucyBsZXMgem9uZXMgdXJiYWluZXMsIHRhbmRpcyBxdWUgbGVzIG9pc2VhdXggZGUgdHlwZSBGb3LDqnQgZXQgQWdyaWNvbGUgc29udCBwbHVzIGZyw6lxdWVudHMgZW4gcMOpcmlwaMOpcmllLiBJbCB5IGF1c3NpIGRlIG5vbWJyZXVzZXMgZXNww6hjZXMgZGUgdHlwZSBab25lIGh1bWlkZSBwcsOocyBkZXMgcG9pbnRzIGQnZWF1LiBJbCB5IGEgZGUgbm9tYnJldXNlcyBlc3DDqGNlcyBnw6luw6lyYWxpc3RlcyBhdSBjZW50cmUtdmlsbGUgZXQgZW4gcMOpcmlwaMOpcmllLiBDZXMgb2JzZXJ2YXRpb25zIHN1Z2fDqHJlbnQgIHF1ZSBsYSBkaXZlcnNpdMOpIGRlcyBlc3DDqGNlcyBlc3QgaW5mbHVlbmPDqWUgcGFyIGxlcyBjYXJhY3TDqXJpc3RpcXVlcyBkZXMgaGFiaXRhdHMuDQoNCk5vdXMgYXZvbnMgw6lnYWxlbWVudCDDqXR1ZGnDqSBsZXMgcsOpZ2ltZXMgYWxpbWVudGFpcmVzIGRlcyBvaXNlYXV4IGV0IGF2b25zIGNvbnN0YXTDqSBxdWUgbGUgcsOpZ2ltZSBhbGltZW50YWlyZSBkZSBsJ29pc2VhdSBuZSBzZW1ibGUgcGFzIMOqdHJlIGltcGFjdMOpIHBhciBsJ2FydGlmaWNpYWxpc2F0aW9uIGRlcyBzb2xzLiBDZXR0ZSBvYnNlcnZhdGlvbiBzdWdnw6hyZSBxdWUgbGVzIG9pc2VhdXggc29udCBjYXBhYmxlcyBkZSBzJ2FkYXB0ZXIgw6AgbGV1ciBlbnZpcm9ubmVtZW50IGV0IGRlIHRyb3V2ZXIgZGUgbGEgbm91cnJpdHVyZSBtw6ptZSBkYW5zIGxlcyB6b25lcyB1cmJhaW5lcy4NCg0KRW5maW4sIG5vdXMgYXZvbnMgw6l0dWRpw6kgbGVzIG1vZGVzIGRlIG5pZGlmaWNhdGlvbiBkZXMgb2lzZWF1eCBldCBhdm9ucyBjb25zdGF0w6kgcXVlIGxlcyB6b25lcyBxdWkgY29tYmluZW50IMOgIGxhIGZvaXMgdsOpZ8OpdGF0aW9uIGV0IGLDonRpbWVudHMgc29udCBwbHVzIHN1c2NlcHRpYmxlcyBkJ2FjY3VlaWxsaXIgZGVzIG9pc2VhdXggYXZlYyBkZXMgbW9kZXMgZGUgbmlkaWZpY2F0aW9uIHZhcmnDqXMuIFBhciBleGVtcGxlLCBsZXMgem9uZXMgY2VudHJhbGVzIHNvbnQgcHJvcGljZXMgw6AgbGEgbmlkaWZpY2F0aW9uIGVuIGNhdml0w6kgZW4gcmFpc29uIGRlIGxhIGRlbnNpdMOpIMOpbGV2w6llIGRlIGLDonRpbWVudHMsIHRhbmRpcyBxdWUgbGVzIHpvbmVzIHDDqXJpcGjDqXJpcXVlcyBvZmZyZW50IHBsdXMgZGUgcG9zc2liaWxpdMOpcyBkZSBuaWRpZmljYXRpb24gZW4gYnVpc3NvbiBldCBkYW5zIGxlcyBhcmJyZXMuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KQ2V0dGUgb2JzZXJ2YXRpb24gc291bGlnbmUgbCdpbXBvcnRhbmNlIGRlIHByw6lzZXJ2ZXIgZXQgZGUgcmVzdGF1cmVyIGxlcyBoYWJpdGF0cyBuYXR1cmVscyBkYW5zIGxlcyB6b25lcyB1cmJhaW5lcyBwb3VyIG1haW50ZW5pciB1biBuaXZlYXUgbWF4aW1hbCBkZSBkaXZlcnNpdMOpIGRlcyBlc3DDqGNlcyBkJ29pc2VhdXguDQoNCkVuIGNvbmNsdXNpb24sIG5vdHJlIMOpdHVkZSBhIHBlcm1pcyBkZSBtZXR0cmUgZW4gw6l2aWRlbmNlIGwnaW1wb3J0YW5jZSBkZSBwcsOpc2VydmVyIGV0IGRlIHJlc3RhdXJlciBsZXMgaGFiaXRhdHMgbmF0dXJlbHMgZGFucyBsZXMgem9uZXMgdXJiYWluZXMgcG91ciBtYWludGVuaXIgbGEgYmlvZGl2ZXJzaXTDqSBkZXMgb2lzZWF1eC4gTm91cyBhdm9ucyBjb25zdGF0w6kgcXVlIGxlcyB6b25lcyBkZSB0cmFuc2l0aW9uIGVudHJlIGxlcyBlc3BhY2VzIHVyYmFpbnMgZXQgcnVyYXV4IHNvbnQgZGVzIHpvbmVzIGNsw6lzIHBvdXIgbGEgZGl2ZXJzaXTDqSBkZXMgZXNww6hjZXMgZCdvaXNlYXV4LiBOb3VzIGF2b25zIMOpZ2FsZW1lbnQgb2JzZXJ2w6kgcXVlIGxlcyBvaXNlYXV4IHNvbnQgY2FwYWJsZXMgZGUgcydhZGFwdGVyIMOgIGxldXIgZW52aXJvbm5lbWVudCBldCBkZSB0cm91dmVyIGRlIGxhIG5vdXJyaXR1cmUgbcOqbWUgZGFucyBsZXMgem9uZXMgdXJiYWluZXMuIENlcyByw6lzdWx0YXRzIHNvdWxpZ25lbnQgbCdpbXBvcnRhbmNlIGRlIHByZW5kcmUgZW4gY29tcHRlIGxhIGJpb2RpdmVyc2l0w6kgZGFucyBsZXMgcG9saXRpcXVlcyBkJ2Ftw6luYWdlbWVudCB1cmJhaW4gZXQgZGUgcHLDqXNlcnZlciBsZXMgaGFiaXRhdHMgbmF0dXJlbHMgcG91ciBtYWludGVuaXIgbGEgZGl2ZXJzaXTDqSBkZXMgZXNww6hjZXMgZCdvaXNlYXV4LiBFbiB1dGlsaXNhbnQgZGlmZsOpcmVudGVzIG1lc3VyZXMgZGUgZGl2ZXJzaXTDqSwgbm91cyBhdm9ucyBwdSBjb25maXJtZXIgY2VzIHLDqXN1bHRhdHMgZXQgcmVuZm9yY2VyIGxhIHZhbGlkaXTDqSBkZSBub3RyZSDDqXR1ZGUuDQoNCmBgYHtyfQ0KbW9zX3RhYmxlIDwtIHJlYWQuY3N2KCJkYXRhL2JpcmRzL3RoZW1lc19tb3NfZ2lyLnR4dCIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICJcdCIpDQptb3NfdGFibGUgPC0gbW9zX3RhYmxlWywgYygiSUQiLCAiTU9TIildDQptb3NfdGFibGVbIklEIl0gPC0gcGFzdGUwKCJNT1MiLCBtb3NfdGFibGVbLCAiSUQiXSkNCg0KbW9zX3RhYmxlDQpgYGA=